TP-68566 | Outbound calling - Call status widget (#11131)

This commit is contained in:
Varun Jain
2024-06-04 18:16:11 +05:30
committed by GitHub
parent 6be865e714
commit 62c075c5bf
24 changed files with 773 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -26,6 +26,7 @@ import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_CONTROLLER_WIDG
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_FIRESTORE_CLIENT_CHANGES
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_FIRESTORE_FOR_MESSAGE_STATUS
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_FIRESTORE_SERVER_CHANGES
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_FIRESTORE_SERVER_UPDATES
import com.navi.chat.utils.NaviChatAnalytics.Companion.MESSAGE_ADDED_TO_FIRESTORE
import com.navi.chat.utils.NaviChatAnalytics.Companion.MODIFIED
import com.navi.chat.utils.NaviChatAnalytics.Companion.NAVI_CHAT_WIDGET
@@ -197,6 +198,66 @@ class ChatFireStoreDatabase {
}
}
fun listenToFireStoreServerUpdates(path: String, messageOperation: MessageOperation) {
ChatFireStore.getFireStoreReference(path)
.get()
.addOnSuccessListener {
crmEventTracker.sendEvent(
LISTENER_ATTACHED_TO_FIRESTORE_SUCCESSFULLY,
hashMapOf(PATH to path, SOURCE to LISTEN_TO_FIRESTORE_SERVER_UPDATES)
)
listenerToFirestoreChangesList.add(
ChatFireStore.getFireStoreReference(path).addSnapshotListener { value, errorEx
->
if (errorEx != null) {
crmEventTracker.sendEvent(
ERROR_LISTENING_TO_FIRESTORE_CHANGES,
hashMapOf(
PATH to path,
ERROR to errorEx.toString(),
SOURCE to LISTEN_TO_FIRESTORE_SERVER_UPDATES
)
)
return@addSnapshotListener
}
crmEventTracker.sendEvent(
SUCCESS_LISTENING_TO_FIRESTORE_CHANGES,
hashMapOf(PATH to path, SOURCE to LISTEN_TO_FIRESTORE_SERVER_UPDATES)
)
value?.let {
value.documentChanges.forEach { dc ->
when (dc.type) {
DocumentChange.Type.ADDED -> {}
DocumentChange.Type.MODIFIED -> {
if (
dc.document.data[NAVI_CHAT_WIDGET_NAME] ==
NaviChatCallStatusWidget.WIDGET_NAME
) {
messageOperation.updateCallStatusWidget(
transformFirestoreToWidgetModel(dc.document.data)
)
}
}
DocumentChange.Type.REMOVED -> {}
}
}
}
}
)
}
.addOnFailureListener {
crmEventTracker.sendEvent(
NOT_ABLE_TO_ATTACH_LISTENER_TO_FIRESTORE,
hashMapOf(
PATH to path,
ERROR to it.toString(),
SOURCE to LISTEN_TO_FIRESTORE_SERVER_UPDATES
)
)
}
}
fun listenToFirestoreChanges(
path: String,
latestMessageTimeStamp: Timestamp?,

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -67,6 +67,9 @@ fun transformFirestoreToWidgetModel(data: Map<String, Any>): NaviChatWidget =
convertMapToJson<NaviChatTransactionStatusWidget>(data)
as NaviChatTransactionStatusWidget
}
NaviChatCallStatusWidget.WIDGET_NAME -> {
convertMapToJson<NaviChatCallStatusWidget>(data) as NaviChatCallStatusWidget
}
else -> {
NaviChatMessageWidget()
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -30,4 +30,6 @@ interface MessageOperation {
fun updateMessageReceipts(widgetModel: NaviChatWidget)
fun updateServerMessageReceipts(document: DocumentReference?)
fun updateCallStatusWidget(widgetModel: NaviChatWidget)
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -53,6 +53,10 @@ class FireStoreDataProvider : ChatDataProvider, LifecycleObserver {
path = fireStoreDataProviderModel.writePath,
messageOperation = messageOperation
)
chatFireStoreDatabase.listenToFireStoreServerUpdates(
path = fireStoreDataProviderModel.readPath,
messageOperation = messageOperation
)
}
}

View File

@@ -164,6 +164,7 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
private var botTypingStatusHandler = Handler(Looper.getMainLooper())
private var showCsatHandler = Handler(Looper.getMainLooper())
private var timerHandler = Handler(Looper.getMainLooper())
private var callStatusHandler = Handler(Looper.getMainLooper())
private var textWatcherForTimer: TextWatcher? = null
private var textWatcherForCharLimit: TextWatcher? = null
private var textWatcherForSendAnimation: TextWatcher? = null
@@ -657,6 +658,26 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
}
}
private fun setCallStatusWaitTimeNudgeData(waitTimeConfig: WaitTimeConfig?) {
scrollChatToLatestMessageReceived()
binding.chatCallStatusWaitTimeBreachNudgeLayout.root.setVisibilityState(GONE)
binding.chatCallStatusWaitTimeNudgeLayout.root.setVisibilityState(VISIBLE)
binding.chatCallStatusWaitTimeNudgeLayout.apply {
waitTimeConfig?.imgUrl?.let { ivIcon.showWhenDataIsAvailable(imageUrl = it) }
waitTimeConfig?.waitTimeMessage?.let { tvTitle.text = it }
}
}
private fun setCallStatusWaitTimeBreachNudgeData(waitTimeConfig: WaitTimeConfig?) {
scrollChatToLatestMessageReceived()
binding.chatCallStatusWaitTimeNudgeLayout.root.setVisibilityState(GONE)
binding.chatCallStatusWaitTimeBreachNudgeLayout.root.setVisibilityState(VISIBLE)
binding.chatCallStatusWaitTimeBreachNudgeLayout.apply {
waitTimeConfig?.imgUrl?.let { ivIcon.showWhenDataIsAvailable(imageUrl = it) }
waitTimeConfig?.waitTimeBreachMessage?.let { tvTitle.text = it }
}
}
private fun observeNetworkConnectivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
@@ -703,6 +724,8 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
private fun updateNetworkConnectivityNudge(
networkConnectivityNudgeData: NetworkConnectivityNudgeData
) {
binding.chatCallStatusWaitTimeNudgeLayout.root.setVisibilityState(GONE)
binding.chatCallStatusWaitTimeBreachNudgeLayout.root.setVisibilityState(GONE)
binding.networkConnectivityNudgeLayout.root.setVisibilityState(View.VISIBLE)
binding.networkConnectivityNudgeLayout.root.setOnClickListener {}
@@ -1194,6 +1217,24 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
}
}
override fun updateCallStatusWidget(widgetModel: NaviChatWidget) {
if (
widgetModel.widgetNameForBaseAdapter == NaviChatCallStatusWidget.WIDGET_NAME &&
widgetModel is NaviChatCallStatusWidget
) {
chatRVAdapter.list
.findIndexed { _, it ->
(it is NaviChatCallStatusWidget) && (it.messageId == widgetModel.messageId)
}
.apply {
this?.let {
(it.first as? NaviChatCallStatusWidget)?.widgetData = widgetModel.widgetData
updateItemInChatAdapter(it.second)
}
}
}
}
override fun updateServerMessageReceipts(document: DocumentReference?) {
try {
if (updateMessageReceipts) {
@@ -1628,6 +1669,9 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
hideKeyboard(context = context, view = binding.etFreeText)
}
}
if (naviChatControllerWidget.widgetData?.controllerConfig == null) {
hideWaitTimeNudges()
}
naviChatControllerWidget.widgetData?.controllerConfig?.let { config ->
config.showAttachment?.let { showAttachment ->
if (showAttachment) {
@@ -1696,6 +1740,29 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
)
}
}
if (config.waitTimeConfig == null) {
hideWaitTimeNudges()
}
config.waitTimeConfig?.let { voiceCallConfig ->
if (binding.networkConnectivityNudgeLayout.root.visibility == GONE) {
if (
System.currentTimeMillis().minus(voiceCallConfig.startTime ?: 0) <=
(voiceCallConfig.timerDuration?.toLong() ?: 0L)
) {
callStatusHandler.removeCallbacksAndMessages(null)
val remainingTimeToShowWaitTimeBreachNudge =
(voiceCallConfig.startTime ?: 0L) +
(voiceCallConfig.timerDuration ?: 0L) - System.currentTimeMillis()
setCallStatusWaitTimeNudgeData(voiceCallConfig)
callStatusHandler.postDelayed(
{ setCallStatusWaitTimeBreachNudgeData(voiceCallConfig) },
remainingTimeToShowWaitTimeBreachNudge
)
} else {
setCallStatusWaitTimeBreachNudgeData(voiceCallConfig)
}
}
}
}
naviChatControllerWidget.languageSelectionWidgetData?.let { languageSelectionWidgetData ->
languageSelectionWidgetData.isLanguageSelectionEnabled?.let { isLanguageSelectionEnabled
@@ -1731,6 +1798,11 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
}
}
private fun hideWaitTimeNudges() {
binding.chatCallStatusWaitTimeNudgeLayout.root.visibility = GONE
binding.chatCallStatusWaitTimeBreachNudgeLayout.root.visibility = GONE
}
private fun setTextWatcher() {
textWatcherForTimer =
object : TextWatcher {
@@ -2017,6 +2089,7 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
botTypingStatusHandler.removeCallbacksAndMessages(null)
typingStatusHandler.removeCallbacksAndMessages(null)
showCsatHandler.removeCallbacksAndMessages(null)
callStatusHandler.removeCallbacksAndMessages(null)
fileUploadManager.cancelUploads()
naviChatSharedViewModel.resetNumberOfMessagesSent()
naviChatSharedViewModel.resetNumberOfOptionsSelected()

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -14,6 +14,7 @@ import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.navi.naviwidgets.models.NaviChatWidget
import com.navi.naviwidgets.models.response.NaviChatActivityRedirectionWidget
import com.navi.naviwidgets.models.response.NaviChatCallStatusWidget
import com.navi.naviwidgets.models.response.NaviChatControllerWidget
import com.navi.naviwidgets.models.response.NaviChatConversationStatusWidget
import com.navi.naviwidgets.models.response.NaviChatCsatRatingWidget
@@ -175,6 +176,12 @@ object ChatJsonDeserializer : JsonDeserializer<NaviChatWidget> {
naviChatWidget.rt_created_at = timestamp
naviChatWidget
}
NaviChatCallStatusWidget.WIDGET_NAME -> {
val naviChatWidget =
Gson().fromJson(jsonObject, NaviChatCallStatusWidget::class.java)
naviChatWidget.rt_created_at = timestamp
naviChatWidget
}
else -> {
val naviChatWidget =
Gson().fromJson(jsonObject, NaviChatMessageWidget::class.java)

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -158,6 +158,7 @@ class NaviChatAnalytics private constructor() {
const val FAQ_PAGE_LAND = "faq_page_land"
const val CHATSCREEN_TOP_BACK_BUTTON_CLICK = "chatscreen_top_back_button_click"
const val SESSION_ID = "session_id"
const val LISTEN_TO_FIRESTORE_SERVER_UPDATES = "listen_to_firestore_server_updates"
val naviChatAnalytics: NaviChatAnalytics by lazy { Holder.INSTANCE }
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2024 by Navi Technologies Private Limited
~ * * All rights reserved. Strictly confidential
~ *
~ */
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/light_purple_border_color" />
<corners
android:topLeftRadius="@dimen/dp_8"
android:topRightRadius="@dimen/dp_8" />
</shape>
</item>
<item
android:top="@dimen/dp_1"
android:left="@dimen/dp_1"
android:right="@dimen/dp_1">
<shape android:shape="rectangle">
<solid android:color="@color/light_purple" />
<corners
android:topLeftRadius="@dimen/dp_8"
android:topRightRadius="@dimen/dp_8" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2024 by Navi Technologies Private Limited
~ * * All rights reserved. Strictly confidential
~ *
~ */
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/bannerColor" />
<corners
android:topLeftRadius="@dimen/dp_8"
android:topRightRadius="@dimen/dp_8" />
</shape>
</item>
<item
android:top="@dimen/dp_1"
android:left="@dimen/dp_1"
android:right="@dimen/dp_1">
<shape android:shape="rectangle">
<solid android:color="@color/color_FFF3F0" />
<corners
android:topLeftRadius="@dimen/dp_8"
android:topRightRadius="@dimen/dp_8" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_1"
android:background="@drawable/bg_rounded_top_corner_red_fill_color"
android:paddingVertical="@dimen/dp_12"
android:paddingHorizontal="@dimen/dp_16">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_icon"
android:layout_width="@dimen/dp_12"
android:layout_height="@dimen/dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_call_new_theme" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tv_title"
android:layout_width="@dimen/layout_dp_0"
android:textColor="@color/primaryColor"
android:layout_height="wrap_content"
android:textSize="@dimen/sp_12"
android:fontFamily="@font/tt_medium"
android:layout_marginStart="@dimen/layout_dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="The wait time for your call back is taking longer than expected. We apologise for the delay." />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_top_corner_purple_fill_color"
android:layout_marginTop="@dimen/dp_1"
android:paddingVertical="@dimen/dp_12"
android:paddingHorizontal="@dimen/dp_16">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_icon"
android:layout_width="@dimen/dp_12"
android:layout_height="@dimen/dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_call_new_theme" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tv_title"
android:layout_width="@dimen/layout_dp_0"
android:textColor="@color/primaryColor"
android:layout_height="wrap_content"
android:textSize="@dimen/sp_12"
android:fontFamily="@font/tt_medium"
android:layout_marginStart="@dimen/layout_dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Our support team will be calling you within 3 mins." />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -55,11 +55,18 @@
android:layout_height="@dimen/layout_dp_0"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintBottom_toTopOf="@id/network_connectivity_nudge_layout"
app:layout_constraintBottom_toTopOf="@id/topBarrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/nctLayout" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/topBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="network_connectivity_nudge_layout,chat_call_status_wait_time_nudge_layout,chat_call_status_wait_time_breach_nudge_layout" />
<include
android:id="@+id/network_connectivity_nudge_layout"
layout="@layout/chat_network_connectivity_nudge_layout"
@@ -72,6 +79,37 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/chat_call_status_wait_time_nudge_layout"
layout="@layout/chat_call_status_wait_time_nudge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/layout_dp_4"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/rvChat"
app:layout_constraintBottom_toTopOf="@id/viewSeparator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/chat_call_status_wait_time_breach_nudge_layout"
layout="@layout/chat_call_status_wait_time_breach_nudge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/layout_dp_4"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/rvChat"
app:layout_constraintBottom_toTopOf="@id/viewSeparator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bottomBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="network_connectivity_nudge_layout,chat_call_status_wait_time_nudge_layout,chat_call_status_wait_time_breach_nudge_layout" />
<View
android:id="@+id/viewSeparator"
android:layout_width="match_parent"
@@ -82,7 +120,7 @@
app:layout_constraintBottom_toTopOf="@id/clFreeText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/network_connectivity_nudge_layout" />
app:layout_constraintTop_toBottomOf="@id/bottomBarrier" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clFreeText"

View File

@@ -283,4 +283,5 @@
<color name="light_grey_border_color">#1A3C0050</color>
<color name="bg_light_outrageous_orange">#FFF0EB</color>
<color name="vkyc_volume_prompt_text_color">#4B4966</color>
<color name="light_purple_border_color">#EBE4FF</color>
</resources>

View File

@@ -0,0 +1,34 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.interfaces
import com.google.firebase.Timestamp
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.response.NaviChatMetaData
interface NaviChatCallStatusWidgetInfo {
fun messageId(): String?
fun title(): TextWithStyle?
fun subtitle(): TextWithStyle?
fun metaData(): NaviChatMetaData?
fun senderName(): String?
fun timeStamp(): Timestamp?
fun displayIconUrl(): String?
fun imgUrl(): String?
fun bgColor(): String?
fun borderColor(): String?
}

View File

@@ -0,0 +1,85 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.models.response
import com.google.firebase.Timestamp
import com.google.firebase.firestore.PropertyName
import com.google.firebase.firestore.ServerTimestamp
import com.google.gson.annotations.SerializedName
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.interfaces.NaviChatCallStatusWidgetInfo
import com.navi.naviwidgets.models.NaviChatWidget
import com.navi.naviwidgets.utils.*
import java.io.Serializable
data class NaviChatCallStatusWidget(
@SerializedName(NAVI_CHAT_MESSAGE_ID) override val messageId: String? = null,
@set:PropertyName(NAVI_CHAT_WIDGET_NAME)
@get:PropertyName(NAVI_CHAT_WIDGET_NAME)
@SerializedName(NAVI_CHAT_WIDGET_NAME)
override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@get:PropertyName(NAVI_CHAT_WIDGET_DATA)
@SerializedName(NAVI_CHAT_WIDGET_DATA)
var widgetData: NaviChatCallStatusData? = null,
@get:PropertyName(NAVI_CHAT_SHOULD_DISPLAY_IN_CHAT_HISTORY)
@SerializedName(NAVI_CHAT_SHOULD_DISPLAY_IN_CHAT_HISTORY)
override val shouldDisplayInChatHistory: Boolean = true,
@ServerTimestamp
@SerializedName(NAVI_CHAT_RT_CREATED_AT)
override var rt_created_at: Timestamp? = null,
@get:PropertyName(NAVI_CHAT_SENDER_ID)
@SerializedName(NAVI_CHAT_SENDER_ID)
override val senderId: String? = null,
@get:PropertyName(NAVI_CHAT_SENDER_TYPE)
@SerializedName(NAVI_CHAT_SENDER_TYPE)
override val senderType: String? = null
) : NaviChatWidget, NaviChatCallStatusWidgetInfo, Serializable {
companion object {
const val WIDGET_NAME = "CHAT_CALL_STATUS_WIDGET"
}
override fun messageId(): String? = messageId
override fun title(): TextWithStyle? = widgetData?.title
override fun subtitle(): TextWithStyle? = widgetData?.subtitle
override fun metaData(): NaviChatMetaData? = widgetData?.metaData
override fun senderName(): String? = widgetData?.senderName
override fun timeStamp(): Timestamp? = rt_created_at
override fun displayIconUrl(): String? = widgetData?.displayIconUrl
override fun imgUrl(): String? = widgetData?.imgUrl
override fun bgColor(): String? = widgetData?.bgColor
override fun borderColor(): String? = widgetData?.borderColor
}
data class NaviChatCallStatusData(
@get:PropertyName(NAVI_CHAT_TITLE)
@SerializedName(NAVI_CHAT_TITLE)
val title: TextWithStyle? = null,
@get:PropertyName(SUB_TITLE) @SerializedName(SUB_TITLE) val subtitle: TextWithStyle? = null,
@get:PropertyName(BG_COLOR) @SerializedName(BG_COLOR) val bgColor: String? = null,
@get:PropertyName(IMAGE_URL) @SerializedName(IMAGE_URL) val imgUrl: String? = null,
@get:PropertyName(BORDER_COLOR) @SerializedName(BORDER_COLOR) val borderColor: String? = null,
@SerializedName(NAVI_CHAT_DISPLAY_ICON_URL)
@get:PropertyName(NAVI_CHAT_DISPLAY_ICON_URL)
val displayIconUrl: String? = null,
@get:PropertyName(NAVI_CHAT_META_DATA)
@SerializedName(NAVI_CHAT_META_DATA)
val metaData: NaviChatMetaData? = null,
@get:PropertyName(NAVI_CHAT_SENDER_NAME)
@SerializedName(NAVI_CHAT_SENDER_NAME)
val senderName: String? = null,
) : Serializable

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -79,7 +79,10 @@ data class ControllerConfig(
val deleteUpiDeviceData: Boolean? = null,
@SerializedName(NAVI_CHAT_CHAT_EXIT_CONFIG)
@get:PropertyName(NAVI_CHAT_CHAT_EXIT_CONFIG)
val chatExitConfig: ChatExitConfig? = null
val chatExitConfig: ChatExitConfig? = null,
@SerializedName(NAVI_CHAT_WAIT_TIME_CONFIG)
@get:PropertyName(NAVI_CHAT_WAIT_TIME_CONFIG)
val waitTimeConfig: WaitTimeConfig? = null
) : Serializable
data class ChatExitConfig(
@@ -173,3 +176,17 @@ data class ChatLanguages(
@SerializedName("display_name") val value: String? = null,
@SerializedName("is_selected") val isSelected: Boolean? = null,
) : Serializable
data class WaitTimeConfig(
@SerializedName(NAVI_CHAT_TIMER_DURATION)
@get:PropertyName(NAVI_CHAT_TIMER_DURATION)
val timerDuration: Long? = null,
@SerializedName(START_TIME) @get:PropertyName(START_TIME) val startTime: Long? = null,
@SerializedName(LOGO) @get:PropertyName(LOGO) val imgUrl: String? = null,
@SerializedName(NAVI_CHAT_WAIT_TIME_MESSAGE)
@get:PropertyName(NAVI_CHAT_WAIT_TIME_MESSAGE)
val waitTimeMessage: String? = null,
@SerializedName(NAVI_CHAT_WAIT_TIME_BREACH_MESSAGE)
@get:PropertyName(NAVI_CHAT_WAIT_TIME_BREACH_MESSAGE)
val waitTimeBreachMessage: String? = null
) : Serializable

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2019-2023 by Navi Technologies Limited
* * Copyright © 2019-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -77,6 +77,7 @@ const val NAVI_CHAT_RATING_INPUT_TYPE = "rating_input_type"
const val NAVI_CHAT_RECEIPTS = "receipts"
const val NAVI_CHAT_CONTROLLER_CONFIG = "controller_config"
const val NAVI_CHAT_CHAT_EXIT_CONFIG = "chat_exit_config"
const val NAVI_CHAT_WAIT_TIME_CONFIG = "wait_time_config"
const val ACTIVATION_CONFIG = "activation_config"
const val MIN_MESSAGES_SENT_THRESHOLD = "min_messages_sent_threshold"
const val MIN_OPTIONS_SELECTED_THRESHOLD = "min_options_selected_threshold"
@@ -98,6 +99,8 @@ const val WIDGET_TIME_ELAPSED = "WIDGET_TIME_ELAPSED"
const val NAVI_CHAT_LANGUAGE_SELECTION_WIDGET_DATA = "language_selection_widget_data"
const val NAVI_CHAT_LANGUAGE_SELECTION = "is_language_selection_enabled"
const val NAVI_CHAT_LIST_OF_LANGUAGES = "chat_language_options"
const val NAVI_CHAT_WAIT_TIME_MESSAGE = "wait_time_message"
const val NAVI_CHAT_WAIT_TIME_BREACH_MESSAGE = "wait_time_breach_message"
const val WIDGET_ID = "WIDGET_ID"
const val WIDGET_VIEWED_TIME = "WIDGET_VIEWED_TIME"
const val NODE_UUID = "nodeUuid"
@@ -217,3 +220,8 @@ const val PALE_BLUE = "#BED7FF"
const val LIGHT_PASTEL_BLUE = "#EAF2FF"
const val PRE_DIABETIC_CAROUSEL_CARD_COUNT = 20
const val VIEW_TYPE = "viewType"
const val IMAGE_URL = "image_url"
const val BG_COLOR = "bg_color"
const val SUB_TITLE = "sub_title"
const val BORDER_COLOR = "border_color"
const val START_TIME = "start_time"

View File

@@ -0,0 +1,42 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.viewholder
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutNaviChatCallStatusBinding
import com.navi.naviwidgets.models.response.NaviChatCallStatusWidget
import com.navi.naviwidgets.widgets.NaviChatCallStatusWidgetLayout
class NaviChatCallStatusWidgetVH(private val viewDataBinding: ViewDataBinding) :
BaseViewHolder<NaviChatCallStatusWidget>(view = viewDataBinding.root) {
override fun bind(
model: NaviChatCallStatusWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if (itemView is NaviChatCallStatusWidgetLayout) {
(itemView as NaviChatCallStatusWidgetLayout).update(
info = model,
binding = (viewDataBinding as LayoutNaviChatCallStatusBinding),
position = position,
widgetCallback = widgetCallback
)
}
}
override fun bindError(errorData: Any?) {
/*No-op*/
}
override fun bindWidgetStateChanged(payload: Any?) {
/*No-op*/
}
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -49,6 +49,7 @@ class NaviChatViewHolderFactoryImpl<T : NaviBaseAdapterModel>(
private val NAVI_CHAT_TRANSACTION_STATUS_ITEM_WIDGET =
R.layout.layout_navi_chat_transaction_status_message_item
private val NAVI_CHAT_TIMESTAMP_DIVIDER_WIDGET = R.layout.layout_chat_timestamp_divider
private val NAVI_CHAT_CALL_STATUS_WIDGET = R.layout.layout_navi_chat_call_status
}
override fun type(item: NaviBaseAdapterModel?): Int =
@@ -78,6 +79,7 @@ class NaviChatViewHolderFactoryImpl<T : NaviBaseAdapterModel>(
is NaviChatTypingStatusWidget -> NAVI_CHAT_TYPING_STATUS_WIDGET
is NaviChatTransactionStatusWidget -> NAVI_CHAT_TRANSACTION_STATUS_WIDGET
is NaviChatTransactionItemWidget -> NAVI_CHAT_TRANSACTION_STATUS_ITEM_WIDGET
is NaviChatCallStatusWidget -> NAVI_CHAT_CALL_STATUS_WIDGET
else -> {
NaviTrackEvent.trackEvent(
WIDGET_NOT_HANDLED_BY_APP,
@@ -116,6 +118,7 @@ class NaviChatViewHolderFactoryImpl<T : NaviBaseAdapterModel>(
NaviChatTransactionStatusMessageItemVH(viewDataBinding = parent)
NAVI_CHAT_TIMESTAMP_DIVIDER_WIDGET ->
NaviChatTimestampDividerVH(viewDataBinding = parent)
NAVI_CHAT_CALL_STATUS_WIDGET -> NaviChatCallStatusWidgetVH(viewDataBinding = parent)
UNKNOWN_WIDGET -> UnknownWidgetVH(view = parent)
else -> UnknownWidgetVH(view = parent)
}

View File

@@ -0,0 +1,96 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.widgets
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import com.navi.design.R
import com.navi.design.utils.CornerRadius
import com.navi.design.utils.dpToPx
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.parseColorSafe
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutNaviChatCallStatusBinding
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.interfaces.NaviChatCallStatusWidgetInfo
import com.navi.naviwidgets.utils.convertTo12HourFormatTime
import com.navi.naviwidgets.utils.setNaviChatMessageMargin
import com.navi.naviwidgets.utils.updateLogoVisibility
class NaviChatCallStatusWidgetLayout
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
ConstraintLayout(context, attrs, defStyleAttr) {
private lateinit var binding: LayoutNaviChatCallStatusBinding
private lateinit var info: NaviChatCallStatusWidgetInfo
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
fun update(
info: NaviChatCallStatusWidgetInfo,
binding: LayoutNaviChatCallStatusBinding,
position: Int,
widgetCallback: WidgetCallback
) {
this.binding = binding
this.info = info
this.widgetPosition = position
this.widgetCallback = widgetCallback
setProperties()
setMargin()
}
private fun setProperties() {
with(binding) {
updateLogoVisibility(
context,
info.displayIconUrl(),
logoLayout.logo,
logoLayout.cvReceivedMessage
)
tvReceivedMessage.tvCallStatus.showWhenDataIsAvailable(showText = info.title())
tvReceivedMessage.tvCallDuration.showWhenDataIsAvailable(showText = info.subtitle())
info.imgUrl()?.let { imageUrl ->
tvReceivedMessage.ivCallIcon.showWhenDataIsAvailable(imageUrl = imageUrl)
}
tvReceivedMessage.layoutCallStatusItem.background =
getNaviDrawable(
radii =
CornerRadius(
leftTop = dpToPx(TOP_CORNER_RADIUS),
rightTop = dpToPx(TOP_CORNER_RADIUS),
leftBottom = dpToPx(BOTTOM_LEFT_CORNER_RADIUS),
rightBottom = dpToPx(TOP_CORNER_RADIUS)
),
strokeColor = info.borderColor().parseColorSafe(),
strokeWidth = dpToPx(BORDER_WIDTH).toInt(),
backgroundColor = info.bgColor().parseColorSafe()
)
tvTimestamp.showWhenDataIsAvailable(
showText =
info.timeStamp()?.let { timeStamp -> convertTo12HourFormatTime(timeStamp) }
)
}
}
private fun setMargin() {
(binding.root.layoutParams as? RecyclerView.LayoutParams)?.setNaviChatMessageMargin(
bottomMargin = binding.root.context.resources.getInteger(R.integer.integer_8),
topMargin = binding.root.context.resources.getInteger(R.integer.integer_16)
)
}
companion object {
private const val TAG = "NaviChatCallStatusWidgetLayout"
private const val BORDER_WIDTH = 1
private const val TOP_CORNER_RADIUS = 20
private const val BOTTOM_LEFT_CORNER_RADIUS = 4
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ /*
~ * Copyright © 2024 by Navi Technologies Private Limited
~ * All rights reserved. Strictly confidential
~ */
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/dp_20"
android:topRightRadius="@dimen/dp_20"
android:bottomRightRadius="@dimen/dp_20"
android:bottomLeftRadius="@dimen/dp_4"/>
<solid android:color="@color/navi_chat_sent_message_bg_color" />
</shape>

View File

@@ -8,9 +8,10 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/dp_20"
android:bottomRightRadius="@dimen/dp_16"
android:topRightRadius="@dimen/dp_16" />
<corners
android:topLeftRadius="@dimen/dp_20"
android:topRightRadius="@dimen/dp_20"
android:bottomRightRadius="@dimen/dp_20"
android:bottomLeftRadius="@dimen/dp_4"/>
<solid android:color="@color/navi_chat_sent_message_bg_color" />
</shape>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.navi.naviwidgets.widgets.NaviChatCallStatusWidgetLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="@+id/logoLayout"
layout="@layout/layout_navi_chat_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/llReceivedMessage"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/logoLayout"
app:layout_constraintTop_toTopOf="parent">
<include
android:id="@+id/tvReceivedMessage"
layout="@layout/layout_navi_chat_call_status_item"
app:layout_constraintBottom_toTopOf="@id/tvTimestamp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tvTimestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
android:textColor="@color/navi_chat_time_stamp_text_color"
android:textSize="@dimen/font_extra_small"
app:fontFamily="@font/tt_regular"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/tvReceivedMessage"
app:layout_constraintTop_toBottomOf="@id/tvReceivedMessage"
tools:text="10:20" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.navi.naviwidgets.widgets.NaviChatCallStatusWidgetLayout>
</layout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_call_status_item"
android:paddingVertical="@dimen/dp_12"
android:paddingHorizontal="@dimen/dp_16"
android:layout_width="@dimen/dp_200"
tools:backgroundTint="@color/light_purple"
tools:background="@drawable/ic_navi_chat_received_call_message_bg"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivCallIcon"
android:layout_width="@dimen/dp_44"
android:layout_height="@dimen/dp_44"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvCallStatus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tvCallStatus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_10"
android:ellipsize="end"
android:fontFamily="@font/tt_medium"
android:maxLines="@integer/integer_1"
android:textColor="@color/davyGrey"
android:textSize="@dimen/layout_sp_14"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@id/tvCallDuration"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivCallIcon"
app:layout_constraintTop_toTopOf="@id/ivCallIcon"
tools:text="Voice Call" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tvCallDuration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_10"
android:ellipsize="end"
android:fontFamily="@font/tt_regular"
android:maxLines="@integer/integer_1"
android:layout_marginTop="@dimen/dp_4"
android:textColor="@color/davyGrey"
android:textSize="@dimen/layout_sp_14"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@id/ivCallIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivCallIcon"
app:layout_constraintTop_toBottomOf="@id/tvCallStatus"
tools:text="1:30" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>