visitorCallback, Con
});
}
+ /**
+ * @return current instance of {@link CustomCardAdapter}
+ */
+ @Nullable
+ public static CustomCardAdapter getCustomCardAdapter() {
+ return customCardAdapter;
+ }
+
/**
* Allows configuring custom response cards based on metadata.
*
@@ -288,14 +282,6 @@ public static void setCustomCardAdapter(@Nullable CustomCardAdapter customCardAd
GliaWidgets.customCardAdapter = customCardAdapter;
}
- /**
- * @return current instance of {@link CustomCardAdapter}
- */
- @Nullable
- public static CustomCardAdapter getCustomCardAdapter() {
- return customCardAdapter;
- }
-
/**
* Creates `Authentication` instance for a given JWT token.
*
@@ -365,7 +351,7 @@ private static void setupRxErrorHandler() {
private static void throwUncaughtException(Throwable e) {
Thread.UncaughtExceptionHandler handler =
- Thread.currentThread().getUncaughtExceptionHandler();
+ Thread.currentThread().getUncaughtExceptionHandler();
if (handler != null) {
handler.uncaughtException(Thread.currentThread(), e);
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
index bebb02339..a1b24ed28 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
@@ -339,7 +339,17 @@ data class UiTheme(
@get:Deprecated("Replaced by Unified Ui")
val chatHeadConfiguration: ChatHeadConfiguration? = null,
@get:Deprecated("Replaced by Unified Ui")
- val surveyStyle: SurveyStyle? = null
+ val surveyStyle: SurveyStyle? = null,
+
+ // GVA
+ @ColorRes
+ val gvaQuickReplyBackgroundColor: Int? = null,
+
+ @ColorRes
+ val gvaQuickReplyStrokeColor: Int? = null,
+
+ @ColorRes
+ val gvaQuickReplyTextColor: Int? = null
) : Parcelable {
@@ -404,7 +414,10 @@ data class UiTheme(
gliaChatStartedHeadingTextColor = builder.chatStartedHeadingTextColor,
gliaChoiceCardContentTextConfiguration = builder.choiceCardContentTextConfiguration,
chatHeadConfiguration = builder.chatHeadConfiguration,
- surveyStyle = builder.surveyStyle
+ surveyStyle = builder.surveyStyle,
+ gvaQuickReplyBackgroundColor = builder.gvaQuickReplyBackgroundColor,
+ gvaQuickReplyStrokeColor = builder.gvaQuickReplyStrokeColor,
+ gvaQuickReplyTextColor = builder.gvaQuickReplyTextColor
)
class UiThemeBuilder {
@@ -783,6 +796,19 @@ data class UiTheme(
var surveyStyle: SurveyStyle? = SurveyStyle.Builder().build()
private set
+ // GVA
+ @ColorRes
+ var gvaQuickReplyBackgroundColor: Int? = null
+ private set
+
+ @ColorRes
+ var gvaQuickReplyStrokeColor: Int? = null
+ private set
+
+ @ColorRes
+ var gvaQuickReplyTextColor: Int? = null
+ private set
+
fun setAppBarTitle(appBarTitle: String?) {
this.appBarTitle = appBarTitle
}
@@ -1023,6 +1049,19 @@ data class UiTheme(
this.surveyStyle = surveyStyle
}
+
+ fun setGvaQuickReplyBackgroundColor(@ColorRes gvaQuickReplyBackgroundColor: Int?) {
+ this.gvaQuickReplyBackgroundColor = gvaQuickReplyBackgroundColor
+ }
+
+ fun setGvaQuickReplyStrokeColor(@ColorRes gvaQuickReplyStrokeColor: Int?) {
+ this.gvaQuickReplyStrokeColor = gvaQuickReplyStrokeColor
+ }
+
+ fun setGvaQuickReplyTextColor(@ColorRes gvaQuickReplyTextColor: Int?) {
+ this.gvaQuickReplyTextColor = gvaQuickReplyTextColor
+ }
+
fun setTheme(theme: UiTheme) {
appBarTitle = theme.appBarTitle
brandPrimaryColor = theme.brandPrimaryColor
@@ -1080,6 +1119,9 @@ data class UiTheme(
choiceCardContentTextConfiguration = theme.gliaChoiceCardContentTextConfiguration
chatHeadConfiguration = theme.chatHeadConfiguration
surveyStyle = theme.surveyStyle
+ gvaQuickReplyBackgroundColor = theme.gvaQuickReplyBackgroundColor
+ gvaQuickReplyStrokeColor = theme.gvaQuickReplyStrokeColor
+ gvaQuickReplyTextColor = theme.gvaQuickReplyTextColor
}
fun build(): UiTheme {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallController.java b/widgetssdk/src/main/java/com/glia/widgets/call/CallController.java
index 9f67c1172..a23668977 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallController.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallController.java
@@ -231,6 +231,9 @@ public void onHoldChanged(boolean isOnHold) {
public void engagementEnded() {
Logger.d(TAG, "engagementEndedByOperator");
stop();
+ if (!isOngoingEngagementUseCase.invoke()) {
+ dialogController.dismissDialogs();
+ }
}
@Override
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt
new file mode 100644
index 000000000..2c958e05a
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt
@@ -0,0 +1,368 @@
+package com.glia.widgets.chat
+
+import android.text.format.DateUtils
+import androidx.annotation.VisibleForTesting
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.VisitorMessage
+import com.glia.widgets.chat.domain.AddNewMessagesDividerUseCase
+import com.glia.widgets.chat.domain.AppendHistoryChatMessageUseCase
+import com.glia.widgets.chat.domain.AppendNewChatMessageUseCase
+import com.glia.widgets.chat.domain.GliaLoadHistoryUseCase
+import com.glia.widgets.chat.domain.GliaOnMessageUseCase
+import com.glia.widgets.chat.domain.HandleCustomCardClickUseCase
+import com.glia.widgets.chat.domain.IsAuthenticatedUseCase
+import com.glia.widgets.chat.domain.SendUnsentMessagesUseCase
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.CustomCardChatItem
+import com.glia.widgets.chat.model.GvaButton
+import com.glia.widgets.chat.model.GvaQuickReplies
+import com.glia.widgets.chat.model.MediaUpgradeStartedTimerItem
+import com.glia.widgets.chat.model.NewMessagesDividerItem
+import com.glia.widgets.chat.model.OperatorChatItem
+import com.glia.widgets.chat.model.OperatorMessageItem
+import com.glia.widgets.chat.model.OperatorStatusItem
+import com.glia.widgets.chat.model.Unsent
+import com.glia.widgets.core.engagement.domain.model.ChatHistoryResponse
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.core.secureconversations.domain.MarkMessagesReadWithDelayUseCase
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import io.reactivex.BackpressureStrategy
+import io.reactivex.Flowable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import io.reactivex.processors.BehaviorProcessor
+import io.reactivex.processors.PublishProcessor
+import io.reactivex.schedulers.Schedulers
+
+internal class ChatManager constructor(
+ private val onMessageUseCase: GliaOnMessageUseCase,
+ private val loadHistoryUseCase: GliaLoadHistoryUseCase,
+ private val addNewMessagesDividerUseCase: AddNewMessagesDividerUseCase,
+ private val markMessagesReadWithDelayUseCase: MarkMessagesReadWithDelayUseCase,
+ private val appendHistoryChatMessageUseCase: AppendHistoryChatMessageUseCase,
+ private val appendNewChatMessageUseCase: AppendNewChatMessageUseCase,
+ private val sendUnsentMessagesUseCase: SendUnsentMessagesUseCase,
+ private val handleCustomCardClickUseCase: HandleCustomCardClickUseCase,
+ private val isAuthenticatedUseCase: IsAuthenticatedUseCase,
+ private val compositeDisposable: CompositeDisposable = CompositeDisposable(),
+ private val state: BehaviorProcessor = BehaviorProcessor.create(),
+ private val quickReplies: BehaviorProcessor> = BehaviorProcessor.create(),
+ private val action: PublishProcessor = PublishProcessor.create()
+) {
+ fun initialize(
+ onHistoryLoaded: (hasHistory: Boolean) -> Unit,
+ onQuickReplyReceived: (List) -> Unit,
+ onOperatorMessageReceived: (count: Int) -> Unit
+ ): Flowable> {
+
+ subscribe(onHistoryLoaded, onOperatorMessageReceived, onQuickReplyReceived)
+
+ return state.doOnNext(::updateQuickReplies).map(State::immutableChatItems).onBackpressureLatest().share()
+ }
+
+ @VisibleForTesting
+ fun subscribe(
+ onHistoryLoaded: (hasHistory: Boolean) -> Unit,
+ onOperatorMessageReceived: (count: Int) -> Unit,
+ onQuickReplyReceived: (List) -> Unit
+ ) {
+ subscribeToState(onHistoryLoaded, onOperatorMessageReceived).also(compositeDisposable::add)
+ subscribeToQuickReplies(onQuickReplyReceived).also(compositeDisposable::add)
+ }
+
+ fun reset() {
+ state.onNext(State())
+ quickReplies.onNext(emptyList())
+ compositeDisposable.clear()
+ }
+
+ fun onChatAction(action: Action) {
+ this.action.onNext(action)
+ }
+
+ @VisibleForTesting
+ fun subscribeToState(onHistoryLoaded: (hasHistory: Boolean) -> Unit, onOperatorMessageReceived: (count: Int) -> Unit): Disposable = state.run {
+ loadHistory(onHistoryLoaded)
+ .concatWith(subscribeToMessages(onOperatorMessageReceived))
+ .doOnError { it.printStackTrace() }
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.computation())
+ .subscribe(::onNext, ::onError)
+ }
+
+ @VisibleForTesting
+ fun loadHistory(onHistoryLoaded: (hasHistory: Boolean) -> Unit): Flowable = loadHistoryUseCase()
+ .map { mapChatHistory(it) }
+ .doOnSuccess { onHistoryLoaded(it.chatItems.isNotEmpty()) }
+ .toFlowable()
+
+ @VisibleForTesting
+ fun subscribeToMessages(onOperatorMessageReceived: (count: Int) -> Unit): Flowable = Flowable.merge(onMessage(), onAction())
+ .doOnNext { onOperatorMessageReceived(it.addedMessagesCount) }
+
+ @VisibleForTesting
+ fun updateQuickReplies(state: State) {
+ state.run { chatItems.lastOrNull() as? GvaQuickReplies }
+ ?.run { options }
+ .orEmpty()
+ .also(quickReplies::onNext)
+ }
+
+ @VisibleForTesting
+ fun subscribeToQuickReplies(onQuickReplyReceived: (List) -> Unit): Disposable = quickReplies
+ .distinctUntilChanged()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe { onQuickReplyReceived(it) }
+
+ @VisibleForTesting
+ fun onMessage(): Flowable = onMessageUseCase().toFlowable(BackpressureStrategy.BUFFER).withLatestFrom(state, ::mapNewMessage)
+
+ @VisibleForTesting
+ fun onAction(): Flowable = action.withLatestFrom(state, ::mapAction)
+
+ @VisibleForTesting
+ fun checkUnsentMessages(state: State) {
+ sendUnsentMessagesUseCase(state.unsentItems.firstOrNull() ?: return) {
+ onChatAction(Action.MessageSent(it))
+ }
+ }
+
+ @VisibleForTesting
+ fun mapChatHistory(historyResponse: ChatHistoryResponse, currentState: State? = null): State {
+ val state: State = currentState ?: State()
+
+ if (historyResponse.items.isEmpty()) return state
+
+ val chatItems: MutableList = mutableListOf()
+
+ val rawItems = historyResponse.items
+
+
+ for (index in rawItems.indices.reversed()) {
+
+ val rawMessage = rawItems[index]
+
+ if (state.isNew(rawMessage)) {
+ appendHistoryChatMessageUseCase(chatItems, rawMessage, index == rawItems.lastIndex)
+ }
+ }
+
+ chatItems.reverse()
+
+ if (addNewMessagesDividerUseCase(chatItems, historyResponse.newMessagesCount)) {
+ markMessagesReadWithDelay()
+ }
+
+ state.lastMessageWithVisibleOperatorImage = chatItems.lastOrNull() as? OperatorChatItem
+ state.chatItems.addAll(chatItems)
+
+ return state
+ }
+
+ @VisibleForTesting
+ fun mapNewMessage(chatMessage: ChatMessageInternal, messagesState: State): State {
+ if (messagesState.isNew(chatMessage)) {
+ appendNewChatMessageUseCase(messagesState, chatMessage)
+ if (chatMessage.chatMessage is VisitorMessage) {
+ checkUnsentMessages(messagesState)
+ }
+ }
+
+ return messagesState
+ }
+
+ @VisibleForTesting
+ fun mapAction(action: Action, state: State): State {
+ return when (action) {
+ is Action.QueuingStarted -> mapInQueue(action.companyName, state)
+ is Action.OperatorConnected -> mapOperatorConnected(action, state)
+ Action.Transferring -> mapTransferring(state)
+ is Action.OperatorJoined -> mapOperatorJoined(action, state)
+ is Action.UnsentMessageReceived -> addUnsentMessage(action.message, state)
+ is Action.ResponseCardClicked -> mapResponseCardClicked(action.responseCard, state)
+ is Action.OnMediaUpgradeStarted -> mapMediaUpgrade(action.isVideo, state)
+ Action.OnMediaUpgradeToVideo -> mapUpgradeMediaToVideo(state)
+ Action.OnMediaUpgradeCanceled -> mapMediaUpgradeCanceled(state)
+ is Action.OnMediaUpgradeTimerUpdated -> mapMediaUpgradeTimerUpdated(action.formattedValue, state)
+ is Action.CustomCardClicked -> mapCustomCardClicked(action, state)
+ Action.ChatRestored -> state
+ is Action.MessageSent -> mapMessageSent(action.message, state)
+ }
+ }
+
+ @VisibleForTesting
+ fun mapMessageSent(message: VisitorMessage, state: State): State = mapNewMessage(ChatMessageInternal(message), state)
+
+ @VisibleForTesting
+ fun mapCustomCardClicked(action: Action.CustomCardClicked, state: State): State = action.run {
+ handleCustomCardClickUseCase(customCard, attachment, state)
+ }
+
+ @VisibleForTesting
+ fun mapMediaUpgradeTimerUpdated(formattedValue: String, state: State): State = state.apply {
+ val oldItem = state.mediaUpgradeTimerItem ?: return@apply
+
+ if (oldItem.time == formattedValue) return@apply
+
+ val newItem = oldItem.updateTime(formattedValue)
+
+ mediaUpgradeTimerItem = newItem
+
+ val index = chatItems.indexOf(oldItem)
+
+ if (index == -1) {
+ chatItems += newItem
+ } else {
+ chatItems[index] = newItem
+ }
+
+ }
+
+ @VisibleForTesting
+ fun mapMediaUpgradeCanceled(state: State): State = state.apply {
+ val oldItem = mediaUpgradeTimerItem
+ mediaUpgradeTimerItem = null
+ chatItems -= oldItem ?: return@apply
+ }
+
+ @VisibleForTesting
+ fun mapUpgradeMediaToVideo(state: State): State = state.apply {
+ val oldItem = this.mediaUpgradeTimerItem
+ val newItem = MediaUpgradeStartedTimerItem.Video(oldItem?.time ?: DateUtils.formatElapsedTime(0))
+ mediaUpgradeTimerItem = newItem
+ chatItems += newItem
+ chatItems -= oldItem ?: return@apply
+ }
+
+ @VisibleForTesting
+ fun mapMediaUpgrade(video: Boolean, state: State): State = state.apply {
+ val mediaUpgradeTimerItem = if (video) MediaUpgradeStartedTimerItem.Video() else MediaUpgradeStartedTimerItem.Audio()
+ this.mediaUpgradeTimerItem = mediaUpgradeTimerItem
+ chatItems += mediaUpgradeTimerItem
+ }
+
+ @VisibleForTesting
+ fun mapOperatorJoined(action: Action.OperatorJoined, state: State): State = state.apply {
+ chatItems += action.run {
+ OperatorStatusItem.Joined(companyName, operatorFormattedName, operatorImageUrl)
+ }
+ }
+
+ @VisibleForTesting
+ fun mapResponseCardClicked(responseCard: OperatorMessageItem.ResponseCard, state: State): State = state.apply {
+ val index = chatItems.indexOf(responseCard)
+ chatItems[index] = responseCard.asPlainText()
+ }
+
+ @VisibleForTesting
+ fun addUnsentMessage(message: Unsent, state: State): State {
+ state.unsentItems += message
+ return state.apply {
+ val index = if (chatItems.lastOrNull() is OperatorStatusItem.InQueue) chatItems.lastIndex else chatItems.lastIndex + 1
+ chatItems.add(index, message.chatMessage)
+ }
+ }
+
+ @VisibleForTesting
+ fun mapOperatorConnected(action: Action.OperatorConnected, state: State): State {
+ val operatorStatusItem = action.run { OperatorStatusItem.Connected(companyName, operatorFormattedName, operatorImageUrl) }
+ val oldOperatorStatusItem: OperatorStatusItem? = state.operatorStatusItem
+ state.operatorStatusItem = operatorStatusItem
+
+ checkUnsentMessages(state)
+
+ if (oldOperatorStatusItem != null) {
+ val index = state.chatItems.indexOf(oldOperatorStatusItem)
+
+ if (index != -1) {
+ state.chatItems[index] = operatorStatusItem
+ return state
+ }
+ }
+
+ state.chatItems += operatorStatusItem
+
+ return state
+ }
+
+ @VisibleForTesting
+ fun mapTransferring(state: State): State = state.apply {
+ operatorStatusItem?.also { chatItems -= it }
+ operatorStatusItem = OperatorStatusItem.Transferring.also {
+ chatItems += it
+ }
+ }
+
+ @VisibleForTesting
+ fun mapInQueue(companyName: String, state: State): State = state.apply {
+ OperatorStatusItem.InQueue(companyName).also {
+ operatorStatusItem = it
+ chatItems += it
+ }
+ }
+
+ @VisibleForTesting
+ fun markMessagesReadWithDelay() {
+ val disposable = markMessagesReadWithDelayUseCase()
+ .toSingleDefault(Unit)
+ .toFlowable()
+ .withLatestFrom(state) { _, messagesState: State -> removeNewMessagesDivider(messagesState) }
+ .subscribe(state::onNext) { it.printStackTrace() }
+ compositeDisposable.add(disposable)
+ }
+
+ @VisibleForTesting
+ fun removeNewMessagesDivider(messagesState: State) = messagesState.apply {
+ chatItems.remove(NewMessagesDividerItem)
+ }
+
+ fun reloadHistoryIfNeeded() {
+ if (isAuthenticatedUseCase()) return
+
+ compositeDisposable.add(
+ loadHistoryUseCase().map { mapChatHistory(it, state.value) }
+ .subscribe({ state.onNext(it) }) { Logger.e(TAG, "Chat reload failed", it) }
+ )
+ }
+
+ internal data class State(
+ val chatItems: MutableList = mutableListOf(),
+ val chatItemIds: MutableSet = mutableSetOf(),
+ val unsentItems: MutableList = mutableListOf(),
+ var lastMessageWithVisibleOperatorImage: OperatorChatItem? = null,
+ var operatorStatusItem: OperatorStatusItem? = null,
+ var mediaUpgradeTimerItem: MediaUpgradeStartedTimerItem? = null,
+ var addedMessagesCount: Int = 0
+ ) {
+ val immutableChatItems: List get() = chatItems.toList()
+
+ fun isNew(chatMessageInternal: ChatMessageInternal): Boolean = chatItemIds.add(chatMessageInternal.chatMessage.id)
+
+ fun isOperatorChanged(operatorChatItem: OperatorChatItem): Boolean = lastMessageWithVisibleOperatorImage.let {
+ lastMessageWithVisibleOperatorImage = operatorChatItem
+ it?.operatorId != operatorChatItem.operatorId
+ }
+
+ fun resetOperator() {
+ lastMessageWithVisibleOperatorImage = null
+ }
+ }
+
+ internal sealed interface Action {
+ data class QueuingStarted(val companyName: String) : Action
+ data class OperatorConnected(val companyName: String, val operatorFormattedName: String, val operatorImageUrl: String?) : Action
+ object Transferring : Action
+ data class OperatorJoined(val companyName: String, val operatorFormattedName: String, val operatorImageUrl: String?) : Action
+ data class UnsentMessageReceived(val message: Unsent) : Action
+ data class ResponseCardClicked(val responseCard: OperatorMessageItem.ResponseCard) : Action
+ data class OnMediaUpgradeStarted(val isVideo: Boolean) : Action
+ data class OnMediaUpgradeTimerUpdated(val formattedValue: String) : Action
+ object OnMediaUpgradeToVideo : Action
+ object OnMediaUpgradeCanceled : Action
+ data class CustomCardClicked(val customCard: CustomCardChatItem, val attachment: SingleChoiceAttachment) : Action
+ object ChatRestored : Action
+ data class MessageSent(val message: VisitorMessage) : Action
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
index d68fad930..064cde9a0 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
@@ -17,6 +17,7 @@ import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.View
+import android.view.accessibility.AccessibilityEvent
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
@@ -44,14 +45,14 @@ import com.glia.widgets.chat.adapter.ChatAdapter
import com.glia.widgets.chat.adapter.ChatAdapter.OnCustomCardResponse
import com.glia.widgets.chat.adapter.ChatAdapter.OnFileItemClickListener
import com.glia.widgets.chat.adapter.ChatAdapter.OnImageItemClickListener
+import com.glia.widgets.chat.adapter.ChatItemHeightManager
import com.glia.widgets.chat.adapter.UploadAttachmentAdapter
-import com.glia.widgets.chat.adapter.holder.WebViewViewHolder
import com.glia.widgets.chat.controller.ChatController
+import com.glia.widgets.chat.model.AttachmentItem
import com.glia.widgets.chat.model.ChatInputMode
+import com.glia.widgets.chat.model.ChatItem
import com.glia.widgets.chat.model.ChatState
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem
-import com.glia.widgets.chat.model.history.VisitorAttachmentItem
+import com.glia.widgets.chat.model.CustomCardChatItem
import com.glia.widgets.core.configuration.GliaSdkConfiguration
import com.glia.widgets.core.dialog.Dialog
import com.glia.widgets.core.dialog.DialogController
@@ -82,7 +83,6 @@ import com.glia.widgets.helper.getFontCompat
import com.glia.widgets.helper.getFullHybridTheme
import com.glia.widgets.helper.hideKeyboard
import com.glia.widgets.helper.insetsController
-import com.glia.widgets.helper.isDownloaded
import com.glia.widgets.helper.layoutInflater
import com.glia.widgets.helper.mapUriToFileAttachment
import com.glia.widgets.helper.requireActivity
@@ -173,17 +173,18 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
}
}
private val onCustomCardResponse =
- OnCustomCardResponse { messageId: String, text: String, value: String ->
- controller?.sendCustomCardResponse(messageId, text, value)
+ OnCustomCardResponse { customCard: CustomCardChatItem, text: String, value: String ->
+ controller?.sendCustomCardResponse(customCard, text, value)
}
private val dataObserver: AdapterDataObserver = object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount)
+
val totalItemCount = adapter.itemCount
val lastIndex = totalItemCount - 1
- if (isInBottom) {
- val holder = binding.chatRecyclerView.findViewHolderForAdapterPosition(lastIndex)
- if (holder is WebViewViewHolder) {
+ if (isInBottom && lastIndex != -1) {
+ val itemViewType = adapter.getItemViewType(lastIndex)
+ if (itemViewType == ChatAdapter.CUSTOM_CARD_TYPE) {
// WebView needs time for calculating the height.
// So to scroll to the bottom, we need to do it with delay.
postDelayed(
@@ -210,6 +211,10 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
)
}
+ private val onGvaButtonsClickListener = ChatAdapter.OnGvaButtonsClickListener {
+ controller?.onGvaButtonClicked(it)
+ }
+
init {
initConfigurations()
bindViews()
@@ -388,8 +393,7 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
updateShowSendButton(chatState)
updateChatEditText(chatState)
updateAppBar(chatState)
- binding.newMessagesIndicatorLayout.isVisible =
- chatState.showMessagesUnseenIndicator()
+ binding.newMessagesIndicatorLayout.isVisible = chatState.showMessagesUnseenIndicator
updateNewMessageOperatorStatusView(chatState.operatorProfileImgUrl)
isInBottom = chatState.isChatInBottom
binding.chatRecyclerView.setInBottom(isInBottom)
@@ -402,6 +406,7 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
binding.operatorTypingAnimationView.isVisible = chatState.isOperatorTyping
updateAttachmentButton(chatState)
+ updateQuickRepliesState(chatState)
}
}
@@ -434,10 +439,12 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
}
override fun smoothScrollToBottom() {
+ if (adapter.itemCount < 1) return
post { binding.chatRecyclerView.smoothScrollToPosition(adapter.itemCount - 1) }
}
override fun scrollToBottomImmediate() {
+ if (adapter.itemCount < 1) return
post { binding.chatRecyclerView.scrollToPosition(adapter.itemCount - 1) }
}
@@ -478,9 +485,64 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
override fun fileIsNotReadyForPreview() {
showToast(context.getString(R.string.glia_view_file_not_ready_for_preview))
}
+
+ override fun showBroadcastNotSupportedToast() {
+ showToast(context.getString(R.string.gva_not_supported))
+ }
+
+ override fun requestOpenUri(uri: Uri) {
+ this@ChatView.requestOpenUri(uri)
+ }
+
+ override fun requestOpenDialer(uri: Uri) {
+ this@ChatView.requestOpenDialer(uri)
+ }
+
+ override fun requestOpenEmailClient(uri: Uri) {
+ this@ChatView.requestOpenEmailClient(uri)
+ }
+ }
+ }
+
+ private fun requestOpenEmailClient(uri: Uri) {
+ val intent = Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse("mailto:")) //This step makes sure that only email apps handle this.
+ .putExtra(Intent.EXTRA_EMAIL, arrayOf(uri.schemeSpecificPart))
+
+ if (intent.resolveActivity(context.packageManager) != null) {
+ context.startActivity(intent)
+ } else {
+ Logger.e(TAG, "No email client, uri - $uri")
+ showToast(context.getString(R.string.glia_dialog_unexpected_error_title))
+ }
+ }
+
+ private fun requestOpenDialer(uri: Uri) {
+ val intent = Intent(Intent.ACTION_DIAL).setData(uri)
+
+ if (intent.resolveActivity(context.packageManager) != null) {
+ context.startActivity(intent)
+ } else {
+ Logger.e(TAG, "No dialer uri - $uri")
+ showToast(context.getString(R.string.glia_dialog_unexpected_error_title))
}
}
+ private fun requestOpenUri(uri: Uri) {
+ Intent(Intent.ACTION_VIEW, uri).addCategory(Intent.CATEGORY_BROWSABLE).also {
+ if (it.resolveActivity(context.packageManager) != null) {
+ context.startActivity(it)
+ } else {
+ Logger.e(TAG, "No app to open url - $uri")
+ showToast(context.getString(R.string.glia_dialog_unexpected_error_title))
+ }
+ }
+ }
+
+ private fun updateQuickRepliesState(chatState: ChatState) {
+ binding.gvaQuickRepliesLayout.setButtons(chatState.gvaQuickReplies)
+ }
+
private fun updateNewMessageOperatorStatusView(operatorProfileImgUrl: String?) {
binding.newMessagesIndicatorImage.apply {
operatorProfileImgUrl?.also(::showProfileImage) ?: showPlaceholder()
@@ -508,6 +570,7 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
Dialog.MODE_ENABLE_SCREEN_SHARING_NOTIFICATIONS_AND_START_SHARING -> post {
showAllowScreenSharingNotificationsAndStartSharingDialog()
}
+
Dialog.MODE_VISITOR_CODE -> {
Logger.e(TAG, "DialogController callback in ChatView with MODE_VISITOR_CODE")
} // Should never happen inside ChatView
@@ -522,24 +585,7 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
}
private fun updateIsFileDownloaded(item: ChatItem): ChatItem = when (item) {
- is OperatorAttachmentItem -> OperatorAttachmentItem(
- item.id,
- item.viewType,
- item.showChatHead,
- item.attachmentFile,
- item.operatorProfileImgUrl,
- item.attachmentFile.isDownloaded(context),
- item.isDownloading,
- item.operatorId,
- item.messageId,
- item.timestamp
- )
-
- is VisitorAttachmentItem -> VisitorAttachmentItem.editDownloadedStatus(
- item,
- item.attachmentFile.isDownloaded(context)
- )
-
+ is AttachmentItem -> item.run { updateWith(isDownloaded(context), isDownloading) }
else -> item
}
@@ -595,12 +641,12 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
},
negativeButtonClickListener = {
dismissAlertDialog()
- controller?.notificationsDialogDismissed()
+ controller?.notificationDialogDismissed()
screenSharingController?.onScreenSharingDeclined()
},
cancelListener = {
it.dismiss()
- controller?.notificationsDialogDismissed()
+ controller?.notificationDialogDismissed()
screenSharingController?.onScreenSharingDeclined()
}
)
@@ -618,16 +664,16 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
negativeButtonText = resources.getString(R.string.glia_dialog_allow_notifications_no),
positiveButtonClickListener = {
dismissAlertDialog()
- controller?.notificationsDialogDismissed()
+ controller?.notificationDialogDismissed()
this.context.openNotificationChannelScreen()
},
negativeButtonClickListener = {
dismissAlertDialog()
- controller?.notificationsDialogDismissed()
+ controller?.notificationDialogDismissed()
},
cancelListener = {
it.dismiss()
- controller?.notificationsDialogDismissed()
+ controller?.notificationDialogDismissed()
}
)
}
@@ -702,6 +748,8 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
this,
this,
onCustomCardResponse,
+ onGvaButtonsClickListener,
+ ChatItemHeightManager(theme, layoutInflater, resources),
GliaWidgets.getCustomCardAdapter(),
Dependencies.getUseCaseFactory().createGetImageFileFromCacheUseCase(),
Dependencies.getUseCaseFactory().createGetImageFileFromDownloadsUseCase(),
@@ -762,6 +810,8 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
binding.operatorTypingAnimationView.addColorFilter(color = it)
}
+ binding.gvaQuickRepliesLayout.updateTheme(theme)
+
applyTheme(Dependencies.getGliaThemeManager().theme)
}
@@ -790,6 +840,15 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
}
binding.appBarView.setOnXClickedListener { controller?.onXButtonClicked() }
binding.newMessagesIndicatorCard.setOnClickListener { controller?.newMessagesIndicatorClicked() }
+ binding.gvaQuickRepliesLayout.onItemClickedListener = GvaChipGroup.OnItemClickedListener {
+ controller?.onGvaButtonClicked(it)
+
+ // move the focus back to the chat list
+ binding.chatRecyclerView.adapter?.itemCount?.let { size ->
+ val viewHolder = binding.chatRecyclerView.findViewHolderForAdapterPosition(size - 1)
+ viewHolder?.itemView?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+ }
+ }
}
private fun setupAddAttachmentButton() {
@@ -1101,30 +1160,11 @@ class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defSty
currentChatItem: ChatItem,
isDownloading: Boolean,
isFileExists: Boolean
- ): ChatItem {
- if (currentChatItem is VisitorAttachmentItem) {
- if (currentChatItem.attachmentFile.id == attachmentFile.id) {
- return VisitorAttachmentItem.editFileStatuses(
- currentChatItem,
- isFileExists,
- isDownloading
- )
- }
- } else if (currentChatItem is OperatorAttachmentItem && currentChatItem.attachmentFile.id == attachmentFile.id) {
- return OperatorAttachmentItem(
- currentChatItem.id,
- currentChatItem.viewType,
- currentChatItem.showChatHead,
- currentChatItem.attachmentFile,
- currentChatItem.operatorProfileImgUrl,
- isFileExists,
- isDownloading,
- currentChatItem.operatorId,
- currentChatItem.messageId,
- currentChatItem.timestamp
- )
- }
- return currentChatItem
+ ): ChatItem = when {
+ currentChatItem is AttachmentItem && currentChatItem.attachmentId == attachmentFile.id ->
+ currentChatItem.updateWith(isFileExists, isDownloading)
+
+ else -> currentChatItem
}
override fun onFileOpenClick(file: AttachmentFile) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatViewCallback.java b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatViewCallback.java
index feb922015..64b6d8e19 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatViewCallback.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatViewCallback.java
@@ -1,15 +1,18 @@
package com.glia.widgets.chat;
+import android.net.Uri;
import android.view.View;
import androidx.annotation.NonNull;
import com.glia.androidsdk.chat.AttachmentFile;
import com.glia.androidsdk.engagement.Survey;
+import com.glia.widgets.chat.model.ChatItem;
import com.glia.widgets.chat.model.ChatState;
-import com.glia.widgets.chat.model.history.ChatItem;
import com.glia.widgets.core.fileupload.model.FileAttachment;
+import org.jetbrains.annotations.NotNull;
+
import java.util.List;
public interface ChatViewCallback {
@@ -43,4 +46,12 @@ public interface ChatViewCallback {
void navigateToPreview(AttachmentFile attachmentFile, View view);
void fileIsNotReadyForPreview();
+
+ void showBroadcastNotSupportedToast();
+
+ void requestOpenUri(@NonNull Uri uri);
+
+ void requestOpenDialer(@NotNull Uri uri);
+
+ void requestOpenEmailClient(@NotNull Uri uri);
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt
new file mode 100644
index 000000000..914cc46cd
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt
@@ -0,0 +1,123 @@
+package com.glia.widgets.chat
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.ViewGroup
+import androidx.core.view.children
+import androidx.core.view.isVisible
+import androidx.transition.TransitionManager
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.model.GvaButton
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.applyShadow
+import com.glia.widgets.helper.getColorCompat
+import com.glia.widgets.helper.wrapWithMaterialThemeOverlay
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
+import com.google.android.material.chip.Chip
+import com.google.android.material.chip.ChipGroup
+import com.google.android.material.transition.MaterialFadeThrough
+
+class GvaChip @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = com.google.android.material.R.attr.chipStyle
+) : Chip(context.wrapWithMaterialThemeOverlay(attrs, defStyleAttr), attrs, defStyleAttr) {
+
+ private val quickReplyTheme: ButtonTheme? by lazy {
+ Dependencies.getGliaThemeManager().theme?.chatTheme?.gva?.quickReplyTheme
+ }
+
+ init {
+ applyQuickReplyTheme()
+ }
+
+ private fun applyQuickReplyTheme() {
+ quickReplyTheme?.also { applyButtonTheme(it) }
+ }
+
+ private fun applyButtonTheme(buttonTheme: ButtonTheme) {
+ applyTextTheme(buttonTheme.text, withAlignment = false)
+ applyBackgroundTheme(buttonTheme.background)
+ buttonTheme.elevation?.also { elevation = it }
+ buttonTheme.shadowColor?.also(::applyShadow)
+ }
+
+ private fun applyBackgroundTheme(background: LayerTheme?) {
+ background?.apply {
+ fill?.also { chipBackgroundColor = it.primaryColorStateList }
+ stroke?.also { chipStrokeColor = ColorStateList.valueOf(it) }
+ borderWidth?.also { chipStrokeWidth = it }
+ cornerRadius?.also { shapeAppearanceModel = shapeAppearanceModel.withCornerSize(it) }
+ }
+ }
+
+ internal fun applyUiTheme(uiTheme: UiTheme?) {
+ with(uiTheme ?: return) {
+ gvaQuickReplyBackgroundColor?.let { setChipBackgroundColorResource(it) }
+ gvaQuickReplyStrokeColor?.let { setChipStrokeColorResource(it) }
+ gvaQuickReplyTextColor?.let { getColorCompat(it) }?.let { setTextColor(it) }
+ applyQuickReplyTheme()
+ }
+ }
+
+}
+
+class GvaChipGroup @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = com.google.android.material.R.attr.chipGroupStyle
+) : ChipGroup(context.wrapWithMaterialThemeOverlay(attrs, defStyleAttr), attrs, defStyleAttr) {
+
+ internal var onItemClickedListener: OnItemClickedListener? = null
+ private var theme: UiTheme? = null
+
+ init {
+ isSelectionRequired = false
+ isSingleLine = false
+ isSingleSelection = false
+ }
+
+ internal fun updateTheme(theme: UiTheme?) {
+ this.theme = theme
+
+ children.forEach { (it as? GvaChip)?.applyUiTheme(theme) }
+ }
+
+ internal fun setButtons(buttons: List) {
+
+ val hasItems = buttons.isNotEmpty()
+
+ if (hasItems) {
+ removeAllViews()
+ buttons.forEach { addButton(it, theme) }
+
+ if (isVisible) return
+
+ TransitionManager.beginDelayedTransition(parent as ViewGroup, MaterialFadeThrough())
+ }
+
+ isVisible = hasItems
+ }
+
+ private fun addButton(gvaButton: GvaButton, uiTheme: UiTheme?) {
+ GvaChip(context).apply {
+ applyUiTheme(uiTheme)
+ text = gvaButton.text
+ setOnClickListener {
+ onItemClickedListener?.onItemClicked(gvaButton)
+ this@GvaChipGroup.isVisible = false
+ }
+
+ addView(this)
+ }
+ }
+
+
+ internal fun interface OnItemClickedListener {
+ fun onItemClicked(gvaButton: GvaButton)
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapter.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapter.kt
index dc71c2990..d8ea77c94 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapter.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapter.kt
@@ -9,6 +9,9 @@ import com.glia.androidsdk.chat.AttachmentFile
import com.glia.widgets.R
import com.glia.widgets.UiTheme
import com.glia.widgets.chat.adapter.holder.CustomCardViewHolder
+import com.glia.widgets.chat.adapter.holder.GvaGalleryViewHolder
+import com.glia.widgets.chat.adapter.holder.GvaPersistentButtonsViewHolder
+import com.glia.widgets.chat.adapter.holder.GvaResponseTextViewHolder
import com.glia.widgets.chat.adapter.holder.MediaUpgradeStartedViewHolder
import com.glia.widgets.chat.adapter.holder.NewMessagesDividerViewHolder
import com.glia.widgets.chat.adapter.holder.OperatorMessageViewHolder
@@ -20,15 +23,22 @@ import com.glia.widgets.chat.adapter.holder.fileattachment.VisitorFileAttachment
import com.glia.widgets.chat.adapter.holder.imageattachment.ImageAttachmentViewHolder
import com.glia.widgets.chat.adapter.holder.imageattachment.OperatorImageAttachmentViewHolder
import com.glia.widgets.chat.adapter.holder.imageattachment.VisitorImageAttachmentViewHolder
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.CustomCardItem
-import com.glia.widgets.chat.model.history.MediaUpgradeStartedTimerItem
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem
-import com.glia.widgets.chat.model.history.OperatorMessageItem
-import com.glia.widgets.chat.model.history.OperatorStatusItem
-import com.glia.widgets.chat.model.history.SystemChatItem
-import com.glia.widgets.chat.model.history.VisitorAttachmentItem
-import com.glia.widgets.chat.model.history.VisitorMessageItem
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.CustomCardChatItem
+import com.glia.widgets.chat.model.GvaButton
+import com.glia.widgets.chat.model.GvaGalleryCards
+import com.glia.widgets.chat.model.GvaPersistentButtons
+import com.glia.widgets.chat.model.GvaQuickReplies
+import com.glia.widgets.chat.model.GvaResponseText
+import com.glia.widgets.chat.model.MediaUpgradeStartedTimerItem
+import com.glia.widgets.chat.model.OperatorAttachmentItem
+import com.glia.widgets.chat.model.OperatorMessageItem
+import com.glia.widgets.chat.model.OperatorStatusItem
+import com.glia.widgets.chat.model.SystemChatItem
+import com.glia.widgets.chat.model.VisitorAttachmentItem
+import com.glia.widgets.chat.model.VisitorMessageItem
+import com.glia.widgets.databinding.ChatGvaGalleryLayoutBinding
+import com.glia.widgets.databinding.ChatGvaPersistentButtonsContentBinding
import com.glia.widgets.databinding.ChatMediaUpgradeLayoutBinding
import com.glia.widgets.databinding.ChatNewMessagesDividerLayoutBinding
import com.glia.widgets.databinding.ChatOperatorMessageLayoutBinding
@@ -47,12 +57,14 @@ internal class ChatAdapter(
private val onFileItemClickListener: OnFileItemClickListener,
private val onImageItemClickListener: OnImageItemClickListener,
private val onCustomCardResponse: OnCustomCardResponse,
+ private val onGvaButtonsClickListener: OnGvaButtonsClickListener,
+ private val chatItemHeightManager: ChatItemHeightManager,
private val customCardAdapter: CustomCardAdapter?,
private val getImageFileFromCacheUseCase: GetImageFileFromCacheUseCase,
private val getImageFileFromDownloadsUseCase: GetImageFileFromDownloadsUseCase,
private val getImageFileFromNetworkUseCase: GetImageFileFromNetworkUseCase
) : RecyclerView.Adapter() {
- private val differ = AsyncListDiffer(this, ChatAdapterDillCallback())
+ private val differ = AsyncListDiffer(this, ChatAdapterDiffCallback())
override fun onCreateViewHolder(
parent: ViewGroup,
@@ -66,11 +78,13 @@ internal class ChatAdapter(
uiTheme
)
}
+
VISITOR_FILE_VIEW_TYPE -> {
val view =
inflater.inflate(R.layout.chat_attachment_visitor_file_layout, parent, false)
VisitorFileAttachmentViewHolder(view, uiTheme)
}
+
VISITOR_IMAGE_VIEW_TYPE -> {
VisitorImageAttachmentViewHolder(
inflater.inflate(R.layout.chat_attachment_visitor_image_layout, parent, false),
@@ -80,12 +94,14 @@ internal class ChatAdapter(
getImageFileFromNetworkUseCase
)
}
+
VISITOR_MESSAGE_TYPE -> {
VisitorMessageViewHolder(
ChatVisitorMessageLayoutBinding.inflate(inflater, parent, false),
uiTheme
)
}
+
OPERATOR_IMAGE_VIEW_TYPE -> {
OperatorImageAttachmentViewHolder(
inflater.inflate(R.layout.chat_attachment_operator_image_layout, parent, false),
@@ -95,6 +111,7 @@ internal class ChatAdapter(
getImageFileFromNetworkUseCase
)
}
+
OPERATOR_FILE_VIEW_TYPE -> {
OperatorFileAttachmentViewHolder(
inflater.inflate(
@@ -105,18 +122,21 @@ internal class ChatAdapter(
uiTheme
)
}
+
OPERATOR_MESSAGE_VIEW_TYPE -> {
OperatorMessageViewHolder(
ChatOperatorMessageLayoutBinding.inflate(inflater, parent, false),
uiTheme
)
}
+
MEDIA_UPGRADE_ITEM_TYPE -> {
MediaUpgradeStartedViewHolder(
ChatMediaUpgradeLayoutBinding.inflate(inflater, parent, false),
uiTheme
)
}
+
NEW_MESSAGES_DIVIDER_TYPE -> {
NewMessagesDividerViewHolder(
ChatNewMessagesDividerLayoutBinding.inflate(
@@ -127,6 +147,7 @@ internal class ChatAdapter(
uiTheme
)
}
+
SYSTEM_MESSAGE_TYPE -> SystemMessageViewHolder(
ChatReceiveMessageContentBinding.inflate(
inflater,
@@ -135,6 +156,47 @@ internal class ChatAdapter(
),
uiTheme
)
+
+ GVA_RESPONSE_TEXT_TYPE, GVA_QUICK_REPLIES_TYPE -> {
+ val operatorMessageBinding = ChatOperatorMessageLayoutBinding.inflate(inflater, parent, false)
+ GvaResponseTextViewHolder(
+ operatorMessageBinding,
+ ChatReceiveMessageContentBinding.inflate(
+ inflater,
+ operatorMessageBinding.contentLayout,
+ true
+ ),
+ uiTheme
+ )
+ }
+
+ GVA_PERSISTENT_BUTTONS_TYPE -> {
+ val operatorMessageBinding = ChatOperatorMessageLayoutBinding.inflate(inflater, parent, false)
+ GvaPersistentButtonsViewHolder(
+ operatorMessageBinding,
+ ChatGvaPersistentButtonsContentBinding.inflate(
+ inflater,
+ operatorMessageBinding.contentLayout,
+ true
+ ),
+ onGvaButtonsClickListener,
+ uiTheme
+ )
+ }
+
+ GVA_GALLERY_CARDS_TYPE -> {
+ GvaGalleryViewHolder(
+ ChatGvaGalleryLayoutBinding.inflate(
+ inflater,
+ parent,
+ false
+ ),
+ onGvaButtonsClickListener,
+ uiTheme
+ )
+ }
+
+
else -> {
var customCardViewHolder: CustomCardViewHolder? = null
if (customCardAdapter != null) {
@@ -152,64 +214,70 @@ internal class ChatAdapter(
}
}
- override fun onBindViewHolder(
- holder: RecyclerView.ViewHolder,
- position: Int,
- payloads: MutableList
- ) {
- if (differ.currentList[position] is MediaUpgradeStartedTimerItem) {
- val time = (payloads.firstOrNull() as? String)
-
- if (time != null) {
- (holder as? MediaUpgradeStartedViewHolder)?.updateTime(time)
- return
- }
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList) {
+ val isHandled: Boolean = when (holder) {
+ is MediaUpgradeStartedViewHolder -> updateMediaUpgradeTimer(payloads, holder)
+ is VisitorMessageViewHolder -> updateDeliveredState(payloads, holder)
+ is VisitorFileAttachmentViewHolder -> updateDeliveredState(payloads, holder)
+ is VisitorImageAttachmentViewHolder -> updateDeliveredState(payloads, holder)
+ else -> false
+ }
+
+ if (!isHandled) {
+ super.onBindViewHolder(holder, position, payloads)
}
- super.onBindViewHolder(holder, position, payloads)
}
+ private fun updateDeliveredState(payloads: MutableList, holder: VisitorMessageViewHolder): Boolean =
+ payloads.lastOrNull { it is Boolean }?.let {
+ holder.updateDelivered(it as Boolean)
+ true
+ } ?: false
+
+ private fun updateDeliveredState(payloads: MutableList, holder: VisitorFileAttachmentViewHolder): Boolean =
+ payloads.lastOrNull { it is Boolean }?.let {
+ holder.updateDelivered(it as Boolean)
+ true
+ } ?: false
+
+ private fun updateDeliveredState(payloads: MutableList, holder: VisitorImageAttachmentViewHolder): Boolean =
+ payloads.lastOrNull { it is Boolean }?.let {
+ holder.updateDelivered(it as Boolean)
+ true
+ } ?: false
+
+ private fun updateMediaUpgradeTimer(payloads: MutableList, viewHolder: MediaUpgradeStartedViewHolder): Boolean = payloads.run {
+ firstOrNull() as? String
+ }?.let {
+ viewHolder.updateTime(it)
+ true
+ } ?: false
+
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val chatItem = differ.currentList[position]) {
is OperatorStatusItem -> (holder as OperatorStatusViewHolder).bind(chatItem)
is VisitorMessageItem -> (holder as VisitorMessageViewHolder).bind(chatItem)
- is OperatorMessageItem -> (holder as OperatorMessageViewHolder).bind(
- chatItem,
- onOptionClickedListener
- )
- is MediaUpgradeStartedTimerItem -> (holder as MediaUpgradeStartedViewHolder).bind(
- chatItem
- )
- is OperatorAttachmentItem -> {
- if (chatItem.getViewType() == OPERATOR_FILE_VIEW_TYPE) {
- (holder as OperatorFileAttachmentViewHolder).bind(
- chatItem,
- onFileItemClickListener
- )
- } else {
- (holder as OperatorImageAttachmentViewHolder).bind(
- chatItem,
- onImageItemClickListener
- )
- }
- }
- is VisitorAttachmentItem -> {
- if (chatItem.getViewType() == VISITOR_FILE_VIEW_TYPE) {
- (holder as VisitorFileAttachmentViewHolder).bind(
- chatItem,
- onFileItemClickListener
- )
- } else {
- val viewHolder = holder as VisitorImageAttachmentViewHolder
- viewHolder.bind(chatItem.attachmentFile, chatItem.showDelivered)
- viewHolder.itemView.setOnClickListener {
- onImageItemClickListener.onImageItemClick(chatItem.attachmentFile, it)
- }
+ is OperatorMessageItem -> (holder as OperatorMessageViewHolder).bind(chatItem, onOptionClickedListener)
+ is MediaUpgradeStartedTimerItem -> (holder as MediaUpgradeStartedViewHolder).bind(chatItem)
+ is OperatorAttachmentItem.Image -> (holder as OperatorImageAttachmentViewHolder).bind(chatItem, onImageItemClickListener)
+ is OperatorAttachmentItem.File -> (holder as OperatorFileAttachmentViewHolder).bind(chatItem, onFileItemClickListener)
+ is VisitorAttachmentItem.File -> (holder as VisitorFileAttachmentViewHolder).bind(chatItem, onFileItemClickListener)
+ is VisitorAttachmentItem.Image -> {
+ val viewHolder = holder as VisitorImageAttachmentViewHolder
+ viewHolder.bind(chatItem.attachmentFile, chatItem.showDelivered)
+ viewHolder.itemView.setOnClickListener {
+ onImageItemClickListener.onImageItemClick(chatItem.attachmentFile, it)
}
}
+
is SystemChatItem -> (holder as SystemMessageViewHolder).bind(chatItem.message)
- is CustomCardItem -> {
+ is GvaResponseText -> (holder as GvaResponseTextViewHolder).bind(chatItem)
+ is GvaQuickReplies -> (holder as GvaResponseTextViewHolder).bind(chatItem.asResponseText())
+ is GvaPersistentButtons -> (holder as GvaPersistentButtonsViewHolder).bind(chatItem)
+ is GvaGalleryCards -> (holder as GvaGalleryViewHolder).bind(chatItem, chatItemHeightManager.getMeasuredHeight(chatItem))
+ is CustomCardChatItem -> {
(holder as CustomCardViewHolder).bind(chatItem.message) { text: String, value: String ->
- onCustomCardResponse.onCustomCardResponse(chatItem.getId(), text, value)
+ onCustomCardResponse.onCustomCardResponse(chatItem, text, value)
}
}
}
@@ -229,6 +297,7 @@ internal class ChatAdapter(
}
fun submitList(items: List?) {
+ chatItemHeightManager.measureHeight(items)
differ.submitList(items)
}
@@ -240,12 +309,16 @@ internal class ChatAdapter(
fun onFileDownloadClick(file: AttachmentFile)
}
- interface OnImageItemClickListener {
+ fun interface OnImageItemClickListener {
fun onImageItemClick(item: AttachmentFile, view: View)
}
fun interface OnCustomCardResponse {
- fun onCustomCardResponse(messageId: String, text: String, value: String)
+ fun onCustomCardResponse(customCard: CustomCardChatItem, text: String, value: String)
+ }
+
+ fun interface OnGvaButtonsClickListener {
+ fun onGvaButtonClicked(gvaButton: GvaButton)
}
companion object {
@@ -259,7 +332,15 @@ internal class ChatAdapter(
const val VISITOR_IMAGE_VIEW_TYPE = 7
const val NEW_MESSAGES_DIVIDER_TYPE = 8
const val SYSTEM_MESSAGE_TYPE = 9
- const val CUSTOM_CARD_TYPE = 10 // Should be the last type with the highest value
+
+ //GVA Types
+ const val GVA_RESPONSE_TEXT_TYPE = 10
+ const val GVA_QUICK_REPLIES_TYPE = 11
+ const val GVA_PERSISTENT_BUTTONS_TYPE = 12
+ const val GVA_GALLERY_CARDS_TYPE = 13
+
+ //Custom Card
+ const val CUSTOM_CARD_TYPE = 14 // Should be the last type with the highest value
}
@IntDef(
@@ -273,7 +354,11 @@ internal class ChatAdapter(
VISITOR_IMAGE_VIEW_TYPE,
NEW_MESSAGES_DIVIDER_TYPE,
SYSTEM_MESSAGE_TYPE,
- CUSTOM_CARD_TYPE
+ CUSTOM_CARD_TYPE,
+ GVA_RESPONSE_TEXT_TYPE,
+ GVA_QUICK_REPLIES_TYPE,
+ GVA_PERSISTENT_BUTTONS_TYPE,
+ GVA_GALLERY_CARDS_TYPE
)
@Retention(AnnotationRetention.SOURCE)
annotation class Type
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDiffCallback.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDiffCallback.kt
new file mode 100644
index 000000000..b9fd760ce
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDiffCallback.kt
@@ -0,0 +1,18 @@
+package com.glia.widgets.chat.adapter
+
+import androidx.recyclerview.widget.DiffUtil
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.MediaUpgradeStartedTimerItem
+import com.glia.widgets.chat.model.VisitorChatItem
+
+internal class ChatAdapterDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean = oldItem.id == newItem.id
+ override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean = oldItem.areContentsTheSame(newItem)
+
+ override fun getChangePayload(oldItem: ChatItem, newItem: ChatItem): Any? = when {
+ oldItem is MediaUpgradeStartedTimerItem.Audio && newItem is MediaUpgradeStartedTimerItem.Audio -> newItem.time
+ oldItem is MediaUpgradeStartedTimerItem.Video && newItem is MediaUpgradeStartedTimerItem.Video -> newItem.time
+ oldItem is VisitorChatItem && newItem is VisitorChatItem -> newItem.showDelivered
+ else -> null
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDillCallback.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDillCallback.kt
deleted file mode 100644
index a406c7066..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatAdapterDillCallback.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.glia.widgets.chat.adapter
-
-import androidx.recyclerview.widget.DiffUtil
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.CustomCardItem
-import com.glia.widgets.chat.model.history.MediaUpgradeStartedTimerItem
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem
-import com.glia.widgets.chat.model.history.OperatorMessageItem
-import com.glia.widgets.chat.model.history.OperatorStatusItem
-import com.glia.widgets.chat.model.history.VisitorAttachmentItem
-import com.glia.widgets.chat.model.history.VisitorMessageItem
-
-class ChatAdapterDillCallback : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
- return oldItem.id == newItem.id
- }
-
- override fun areContentsTheSame(oldItem: ChatItem, newItem: ChatItem): Boolean {
- return when {
- oldItem is OperatorStatusItem && newItem is OperatorStatusItem -> oldItem == newItem
- oldItem is VisitorMessageItem && newItem is VisitorMessageItem -> oldItem == newItem
- oldItem is OperatorMessageItem && newItem is OperatorMessageItem -> oldItem == newItem
- oldItem is MediaUpgradeStartedTimerItem && newItem is MediaUpgradeStartedTimerItem -> oldItem == newItem
- oldItem is OperatorAttachmentItem && newItem is OperatorAttachmentItem -> oldItem == newItem
- oldItem is VisitorAttachmentItem && newItem is VisitorAttachmentItem -> oldItem == newItem
- oldItem is CustomCardItem && newItem is CustomCardItem -> oldItem == newItem
- else -> false
- }
- }
-
- override fun getChangePayload(oldItem: ChatItem, newItem: ChatItem): Any? {
- if (
- oldItem is MediaUpgradeStartedTimerItem &&
- newItem is MediaUpgradeStartedTimerItem &&
- oldItem.type == newItem.type
- ) {
- return newItem.time
- }
-
- return null
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatItemHeightManager.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatItemHeightManager.kt
new file mode 100644
index 000000000..3ca797b76
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/ChatItemHeightManager.kt
@@ -0,0 +1,59 @@
+package com.glia.widgets.chat.adapter
+
+import android.content.res.Resources
+import android.view.LayoutInflater
+import android.view.View
+import androidx.collection.ArrayMap
+import com.glia.widgets.R
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.adapter.holder.GvaGalleryItemViewHolder
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.GvaGalleryCard
+import com.glia.widgets.chat.model.GvaGalleryCards
+import com.glia.widgets.databinding.ChatGvaGalleryItemBinding
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+
+internal class ChatItemHeightManager(
+ private val uiTheme: UiTheme,
+ private val layoutInflater: LayoutInflater,
+ private val resources: Resources,
+ private val unifiedTheme: UnifiedTheme? = Dependencies.getGliaThemeManager().theme
+) {
+ private val measuredHeightsMap = ArrayMap()
+
+ private val gvaGalleryItemViewHolder: GvaGalleryItemViewHolder by lazy {
+ GvaGalleryItemViewHolder(ChatGvaGalleryItemBinding.inflate(layoutInflater), {}, uiTheme, unifiedTheme)
+ }
+
+ private val gvaGalleryCardWidth: Int by lazy {
+ resources.getDimensionPixelOffset(R.dimen.glia_chat_gva_gallery_card_width)
+ }
+
+ fun getMeasuredHeight(chatItem: ChatItem): Int? {
+ return measuredHeightsMap[chatItem]
+ }
+
+ fun measureHeight(chatItems: List?) {
+ chatItems
+ ?.filterIsInstance() // Currently the HeightManager works only for GvaGalleryCards
+ ?.forEach { chatItem ->
+ if (!measuredHeightsMap.contains(chatItem)) {
+ measuredHeightsMap[chatItem] = measureHeight(chatItem)
+ }
+ }
+ }
+
+ private fun measureHeight(gvaGalleryCards: GvaGalleryCards): Int {
+ return gvaGalleryCards.galleryCards.maxOf(::measureHeight)
+ }
+
+ private fun measureHeight(gvaGalleryCard: GvaGalleryCard): Int {
+ gvaGalleryItemViewHolder.bindForMeasure(gvaGalleryCard)
+ gvaGalleryItemViewHolder.itemView.measure(
+ View.MeasureSpec.makeMeasureSpec(gvaGalleryCardWidth, View.MeasureSpec.AT_MOST),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ return gvaGalleryItemViewHolder.itemView.measuredHeight
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaButtonsAdapter.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaButtonsAdapter.kt
new file mode 100644
index 000000000..3173228d2
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaButtonsAdapter.kt
@@ -0,0 +1,59 @@
+package com.glia.widgets.chat.adapter
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.appcompat.widget.LinearLayoutCompat
+import androidx.recyclerview.widget.RecyclerView
+import com.glia.widgets.R
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.model.GvaButton
+import com.glia.widgets.helper.Utils
+import com.glia.widgets.helper.getFontCompat
+import com.glia.widgets.view.unifiedui.applyButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.google.android.material.button.MaterialButton
+
+internal class GvaButtonsAdapter(
+ private val buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener,
+ private val uiTheme: UiTheme,
+ private val buttonTheme: ButtonTheme?
+) : RecyclerView.Adapter() {
+ private var options: List? = null
+
+ fun setOptions(options: List) {
+ this.options = options
+ notifyDataSetChanged()
+ }
+
+ class ButtonViewHolder(
+ private val buttonView: MaterialButton
+ ) : RecyclerView.ViewHolder(buttonView) {
+ fun bind(button: GvaButton, buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener) {
+ buttonView.contentDescription = button.text
+ buttonView.text = button.text
+ buttonView.setOnClickListener {
+ buttonsClickListener.onGvaButtonClicked(button)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ButtonViewHolder {
+ val styleResId = Utils.getAttrResourceId(parent.context, R.attr.gvaOptionButtonStyle)
+ val button = MaterialButton(ContextThemeWrapper(parent.context, styleResId), null, 0).also {
+ it.id = View.generateViewId()
+
+ uiTheme.fontRes?.let(parent::getFontCompat)?.also(it::setTypeface)
+
+ buttonTheme?.also(it::applyButtonTheme)
+ }
+ button.layoutParams = LinearLayoutCompat.LayoutParams(LinearLayoutCompat.LayoutParams.MATCH_PARENT, LinearLayoutCompat.LayoutParams.WRAP_CONTENT)
+ return ButtonViewHolder(button)
+ }
+
+ override fun getItemCount(): Int = options?.size ?: 0
+
+ override fun onBindViewHolder(holder: ButtonViewHolder, position: Int) {
+ options?.get(position)?.let { holder.bind(it, buttonsClickListener) }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaGalleryAdapter.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaGalleryAdapter.kt
new file mode 100644
index 000000000..25d273557
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/GvaGalleryAdapter.kt
@@ -0,0 +1,40 @@
+package com.glia.widgets.chat.adapter
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.adapter.holder.GvaGalleryItemViewHolder
+import com.glia.widgets.chat.model.GvaGalleryCard
+import com.glia.widgets.databinding.ChatGvaGalleryItemBinding
+import com.glia.widgets.helper.layoutInflater
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+
+internal class GvaGalleryAdapter(
+ private val buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener,
+ private val uiTheme: UiTheme,
+ private val unifiedTheme: UnifiedTheme?
+) : RecyclerView.Adapter() {
+ private var galleryCards: List? = null
+
+ fun setGalleryCards(galleryCards: List) {
+ this.galleryCards = galleryCards
+ notifyDataSetChanged()
+ }
+
+ override fun getItemCount() = galleryCards?.count() ?: 0
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GvaGalleryItemViewHolder(
+ ChatGvaGalleryItemBinding.inflate(parent.layoutInflater, parent, false),
+ buttonsClickListener,
+ uiTheme,
+ unifiedTheme
+ )
+
+ override fun onBindViewHolder(holder: GvaGalleryItemViewHolder, position: Int) {
+ galleryCards?.apply {
+ getOrNull(position)?.let {
+ holder.bind(it, position, size)
+ }
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryItemViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryItemViewHolder.kt
new file mode 100644
index 000000000..5c3567833
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryItemViewHolder.kt
@@ -0,0 +1,145 @@
+package com.glia.widgets.chat.adapter.holder
+
+import android.os.Bundle
+import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import androidx.core.view.AccessibilityDelegateCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.glia.widgets.R
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.adapter.ChatAdapter
+import com.glia.widgets.chat.adapter.GvaButtonsAdapter
+import com.glia.widgets.chat.model.GvaGalleryCard
+import com.glia.widgets.databinding.ChatGvaGalleryItemBinding
+import com.glia.widgets.helper.fromHtml
+import com.glia.widgets.helper.getColorCompat
+import com.glia.widgets.helper.getColorStateListCompat
+import com.glia.widgets.helper.getFontCompat
+import com.glia.widgets.helper.load
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+import com.glia.widgets.view.unifiedui.theme.chat.MessageBalloonTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaGalleryCardTheme
+import kotlin.properties.Delegates
+
+internal class GvaGalleryItemViewHolder(
+ private val binding: ChatGvaGalleryItemBinding,
+ buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener,
+ private val uiTheme: UiTheme,
+ private val unifiedTheme: UnifiedTheme?
+) : ViewHolder(binding.root) {
+
+ private var adapter: GvaButtonsAdapter by Delegates.notNull()
+
+ private val operatorTheme: MessageBalloonTheme? by lazy {
+ unifiedTheme?.chatTheme?.operatorMessage
+ }
+
+ private val galleryCardTheme: GvaGalleryCardTheme? by lazy {
+ unifiedTheme?.chatTheme?.gva?.galleryCardTheme
+ }
+
+ init {
+ ViewCompat.setAccessibilityDelegate(
+ binding.root,
+ object : AccessibilityDelegateCompat() {
+ override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
+ if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
+ // Sends an accessibility event of accessibility focus type.
+ host.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+ )
+ adapter = GvaButtonsAdapter(buttonsClickListener, uiTheme, galleryCardTheme?.button)
+ binding.buttonsRecyclerView.adapter = adapter
+ binding.item.apply {
+ uiTheme.operatorMessageBackgroundColor?.let(::getColorStateListCompat)?.also {
+ backgroundTintList = it
+ }
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyLayerTheme(galleryCardTheme?.background ?: operatorTheme?.background)
+ }
+ binding.title.apply {
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setTextColor)
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setLinkTextColor)
+
+ uiTheme.fontRes?.let(::getFontCompat)?.also(::setTypeface)
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyTextTheme(galleryCardTheme?.title ?: operatorTheme?.text)
+ }
+ binding.subtitle.apply {
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setTextColor)
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setLinkTextColor)
+
+ uiTheme.fontRes?.let(::getFontCompat)?.also(::setTypeface)
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyTextTheme(galleryCardTheme?.subtitle ?: operatorTheme?.text)
+ }
+ galleryCardTheme?.image?.also(binding.image::applyLayerTheme)
+ }
+
+ fun bindForMeasure(card: GvaGalleryCard) {
+ bindTexts(card)
+ bindButtons(card)
+ }
+
+ fun bind(card: GvaGalleryCard, position: Int, size: Int) {
+ bindTexts(card)
+ bindButtons(card)
+ bindImage(card)
+ updateContendDescription(card, position, size)
+ }
+
+ private fun bindTexts(card: GvaGalleryCard) {
+ binding.title.text = card.title.fromHtml()
+
+ card.subtitle?.let {
+ binding.subtitle.text = it.fromHtml()
+ binding.subtitle.isVisible = true
+ } ?: run {
+ binding.subtitle.isVisible = false
+ }
+ }
+
+ private fun bindButtons(card: GvaGalleryCard) {
+ adapter.setOptions(card.options)
+ binding.buttonsRecyclerView.isVisible = card.options.isEmpty().not()
+ }
+
+ private fun bindImage(card: GvaGalleryCard) {
+ card.imageUrl?.let {
+ binding.image.load(it)
+ binding.image.isVisible = true
+ } ?: run {
+ binding.image.isVisible = false
+ }
+ }
+
+ private fun updateContendDescription(card: GvaGalleryCard, position: Int, size: Int) {
+ val cardContentDescription = listOf(card.title, card.subtitle)
+ .filter { it?.isNotEmpty() ?: false }
+ .joinToString(separator = ". ")
+
+ itemView.contentDescription = itemView.resources.getString(
+ R.string.gva_gallery_card_message_content_description,
+ cardContentDescription,
+ position + 1,
+ size
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryViewHolder.kt
new file mode 100644
index 000000000..86e26fae7
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaGalleryViewHolder.kt
@@ -0,0 +1,53 @@
+package com.glia.widgets.chat.adapter.holder
+
+import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.LinearSnapHelper
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.adapter.ChatAdapter
+import com.glia.widgets.chat.adapter.GvaGalleryAdapter
+import com.glia.widgets.chat.model.GvaGalleryCard
+import com.glia.widgets.chat.model.GvaGalleryCards
+import com.glia.widgets.databinding.ChatGvaGalleryLayoutBinding
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+
+internal class GvaGalleryViewHolder(
+ private val contentBinding: ChatGvaGalleryLayoutBinding,
+ buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener,
+ uiTheme: UiTheme,
+ unifiedTheme: UnifiedTheme? = Dependencies.getGliaThemeManager().theme
+) : OperatorBaseViewHolder(contentBinding.root, contentBinding.chatHeadView, uiTheme, unifiedTheme) {
+ private val adapter = GvaGalleryAdapter(buttonsClickListener, uiTheme, unifiedTheme)
+
+ init {
+ contentBinding.cardRecyclerView.adapter = adapter
+ contentBinding.cardRecyclerView.layoutManager = LinearLayoutManager(
+ contentBinding.root.context,
+ LinearLayoutManager.HORIZONTAL,
+ false
+ )
+ LinearSnapHelper().attachToRecyclerView(contentBinding.cardRecyclerView)
+ }
+
+ fun bind(item: GvaGalleryCards, measuredHeight: Int?) {
+ updateOperatorStatusView(item)
+
+ setupRecyclerViewHeight(measuredHeight)
+ setupItems(item.galleryCards)
+ }
+
+ private fun setupItems(galleryCards: List) {
+ adapter.setGalleryCards(galleryCards)
+ contentBinding.cardRecyclerView.scrollToPosition(0)
+ }
+
+ private fun setupRecyclerViewHeight(measuredHeight: Int?) {
+ if (measuredHeight != null) {
+ contentBinding.cardRecyclerView.layoutParams.height = measuredHeight
+ } else {
+ contentBinding.cardRecyclerView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
+ }
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaPersistentButtonsViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaPersistentButtonsViewHolder.kt
new file mode 100644
index 000000000..13c6d4d4b
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaPersistentButtonsViewHolder.kt
@@ -0,0 +1,69 @@
+package com.glia.widgets.chat.adapter.holder
+
+import android.view.View
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.adapter.ChatAdapter
+import com.glia.widgets.chat.adapter.GvaButtonsAdapter
+import com.glia.widgets.chat.model.GvaPersistentButtons
+import com.glia.widgets.databinding.ChatGvaPersistentButtonsContentBinding
+import com.glia.widgets.databinding.ChatOperatorMessageLayoutBinding
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.fromHtml
+import com.glia.widgets.helper.getColorCompat
+import com.glia.widgets.helper.getColorStateListCompat
+import com.glia.widgets.helper.getFontCompat
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaPersistentButtonTheme
+import kotlin.properties.Delegates
+
+internal class GvaPersistentButtonsViewHolder(
+ operatorMessageBinding: ChatOperatorMessageLayoutBinding,
+ private val contentBinding: ChatGvaPersistentButtonsContentBinding,
+ buttonsClickListener: ChatAdapter.OnGvaButtonsClickListener,
+ private val uiTheme: UiTheme,
+ unifiedTheme: UnifiedTheme? = Dependencies.getGliaThemeManager().theme
+) : OperatorBaseViewHolder(operatorMessageBinding.root, operatorMessageBinding.chatHeadView, uiTheme, unifiedTheme) {
+
+ private var adapter: GvaButtonsAdapter by Delegates.notNull()
+
+ private val persistentButtonTheme: GvaPersistentButtonTheme? by lazy {
+ unifiedTheme?.chatTheme?.gva?.persistentButtonTheme
+ }
+
+ init {
+ adapter = GvaButtonsAdapter(buttonsClickListener, uiTheme, persistentButtonTheme?.button)
+ contentBinding.buttonsRecyclerView.adapter = adapter
+ contentBinding.root.apply {
+ uiTheme.operatorMessageBackgroundColor?.let(::getColorStateListCompat)?.also {
+ backgroundTintList = it
+ }
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyLayerTheme(persistentButtonTheme?.background ?: operatorTheme?.background)
+ }
+ contentBinding.message.apply {
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setTextColor)
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setLinkTextColor)
+
+ uiTheme.fontRes?.let(::getFontCompat)?.also(::setTypeface)
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyTextTheme(persistentButtonTheme?.title ?: operatorTheme?.text)
+ }
+ }
+
+ fun bind(item: GvaPersistentButtons) {
+ updateOperatorStatusView(item)
+ updateItemContentDescription(item.operatorName, item.content)
+
+ contentBinding.message.text = item.content.fromHtml()
+
+ adapter.setOptions(item.options)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaResponseTextViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaResponseTextViewHolder.kt
new file mode 100644
index 000000000..469b9da79
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/GvaResponseTextViewHolder.kt
@@ -0,0 +1,55 @@
+package com.glia.widgets.chat.adapter.holder
+
+import android.view.View
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.model.GvaResponseText
+import com.glia.widgets.databinding.ChatOperatorMessageLayoutBinding
+import com.glia.widgets.databinding.ChatReceiveMessageContentBinding
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.fromHtml
+import com.glia.widgets.helper.getColorCompat
+import com.glia.widgets.helper.getColorStateListCompat
+import com.glia.widgets.helper.getFontCompat
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+
+internal class GvaResponseTextViewHolder(
+ operatorMessageBinding: ChatOperatorMessageLayoutBinding,
+ private val messageContentBinding: ChatReceiveMessageContentBinding,
+ private val uiTheme: UiTheme,
+ unifiedTheme: UnifiedTheme? = Dependencies.getGliaThemeManager().theme
+) : OperatorBaseViewHolder(operatorMessageBinding.root, operatorMessageBinding.chatHeadView, uiTheme, unifiedTheme) {
+
+ init {
+ setupMessageContentView()
+ }
+
+ private fun setupMessageContentView() {
+ messageContentBinding.root.apply {
+ uiTheme.operatorMessageBackgroundColor?.let(::getColorStateListCompat)?.also {
+ backgroundTintList = it
+ }
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setTextColor)
+ uiTheme.operatorMessageTextColor?.let(::getColorCompat)?.also(::setLinkTextColor)
+
+ uiTheme.fontRes?.let(::getFontCompat)?.also(::setTypeface)
+
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ // Unified Ui
+ applyLayerTheme(operatorTheme?.background)
+ applyTextTheme(operatorTheme?.text)
+ }
+ }
+
+ fun bind(item: GvaResponseText) {
+ updateOperatorStatusView(item)
+ updateMessageContentView(item)
+ updateItemContentDescription(item.operatorName, item.content)
+ }
+
+ private fun updateMessageContentView(item: GvaResponseText) {
+ messageContentBinding.root.text = item.content.fromHtml()
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/MediaUpgradeStartedViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/MediaUpgradeStartedViewHolder.kt
index 241353a3a..08a4fff5d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/MediaUpgradeStartedViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/MediaUpgradeStartedViewHolder.kt
@@ -4,7 +4,7 @@ import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.RecyclerView
import com.glia.widgets.R
import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.model.history.MediaUpgradeStartedTimerItem
+import com.glia.widgets.chat.model.MediaUpgradeStartedTimerItem
import com.glia.widgets.databinding.ChatMediaUpgradeLayoutBinding
import com.glia.widgets.di.Dependencies
import com.glia.widgets.helper.getColorCompat
@@ -65,21 +65,26 @@ internal class MediaUpgradeStartedViewHolder(
}
fun bind(chatItem: MediaUpgradeStartedTimerItem) {
- if (chatItem.type == MediaUpgradeStartedTimerItem.Type.AUDIO) {
- upgradeAudioIcon?.also(binding.iconView::setImageResource)
- binding.iconView.contentDescription =
- itemView.resources.getString(R.string.glia_chat_audio_icon_content_description)
- binding.titleView.text =
- itemView.resources.getString(R.string.glia_chat_upgraded_to_audio_call)
- setMediaUpgradeTheme(chatTheme?.audioUpgrade)
- } else {
- upgradeVideoIcon?.also(binding.iconView::setImageResource)
- binding.iconView.contentDescription =
- itemView.resources.getString(R.string.glia_chat_video_icon_content_description)
- binding.titleView.text =
- itemView.resources.getString(R.string.glia_chat_upgraded_to_video_call)
- setMediaUpgradeTheme(chatTheme?.videoUpgrade)
+ when (chatItem) {
+ is MediaUpgradeStartedTimerItem.Audio -> {
+ upgradeAudioIcon?.also(binding.iconView::setImageResource)
+ binding.iconView.contentDescription =
+ itemView.resources.getString(R.string.glia_chat_audio_icon_content_description)
+ binding.titleView.text =
+ itemView.resources.getString(R.string.glia_chat_upgraded_to_audio_call)
+ setMediaUpgradeTheme(chatTheme?.audioUpgrade)
+ }
+
+ is MediaUpgradeStartedTimerItem.Video -> {
+ upgradeVideoIcon?.also(binding.iconView::setImageResource)
+ binding.iconView.contentDescription =
+ itemView.resources.getString(R.string.glia_chat_video_icon_content_description)
+ binding.titleView.text =
+ itemView.resources.getString(R.string.glia_chat_upgraded_to_video_call)
+ setMediaUpgradeTheme(chatTheme?.videoUpgrade)
+ }
}
+
binding.timerView.text = chatItem.time
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorBaseViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorBaseViewHolder.kt
new file mode 100644
index 000000000..e2d2c8fe6
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorBaseViewHolder.kt
@@ -0,0 +1,66 @@
+package com.glia.widgets.chat.adapter.holder
+
+import android.view.View
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import com.glia.widgets.R
+import com.glia.widgets.UiTheme
+import com.glia.widgets.chat.model.OperatorChatItem
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.view.OperatorStatusView
+import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
+import com.glia.widgets.view.unifiedui.theme.chat.MessageBalloonTheme
+
+internal open class OperatorBaseViewHolder(
+ itemView: View,
+ private val chatHeadView: OperatorStatusView,
+ private val uiTheme: UiTheme,
+ unifiedTheme: UnifiedTheme? = Dependencies.getGliaThemeManager().theme
+) : RecyclerView.ViewHolder(itemView) {
+
+ val operatorTheme: MessageBalloonTheme? by lazy {
+ unifiedTheme?.chatTheme?.operatorMessage
+ }
+
+ init {
+ setupOperatorStatusView()
+ }
+
+ fun updateOperatorStatusView(item: OperatorChatItem) {
+ chatHeadView.isVisible = item.showChatHead
+ if (item.operatorProfileImgUrl != null) {
+ chatHeadView.showProfileImage(item.operatorProfileImgUrl)
+ } else {
+ chatHeadView.showPlaceholder()
+ }
+ }
+
+ fun updateItemContentDescription(operatorName: String?, message: String?) {
+ when {
+ operatorName.isNullOrEmpty() && message.isNullOrEmpty() -> {
+ itemView.contentDescription = null
+ }
+
+ operatorName.isNullOrEmpty().not() -> {
+ itemView.contentDescription = itemView.resources.getString(
+ R.string.glia_chat_operator_name_message_content_description,
+ operatorName,
+ message
+ )
+ }
+
+ else -> {
+ itemView.contentDescription = itemView.resources.getString(
+ R.string.glia_chat_operator_message_content_description,
+ message
+ )
+ }
+ }
+ }
+
+ private fun setupOperatorStatusView() {
+ chatHeadView.setTheme(uiTheme)
+ chatHeadView.setShowRippleAnimation(false)
+ chatHeadView.applyUserImageTheme(operatorTheme?.userImage)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorMessageViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorMessageViewHolder.kt
index 85107ca10..900be8934 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorMessageViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorMessageViewHolder.kt
@@ -9,8 +9,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.glia.widgets.R
import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.model.history.OperatorMessageItem
-import com.glia.widgets.chat.model.history.ResponseCardItem
+import com.glia.widgets.chat.model.OperatorMessageItem
import com.glia.widgets.databinding.ChatOperatorMessageLayoutBinding
import com.glia.widgets.databinding.ChatReceiveMessageContentBinding
import com.glia.widgets.di.Dependencies
@@ -69,16 +68,15 @@ internal class OperatorMessageViewHolder(
onOptionClickedListener: OnOptionClickedListener
) {
binding.contentLayout.removeAllViews()
- if (item is ResponseCardItem) {
- addSingleChoiceCardView(item, onOptionClickedListener)
- } else {
- addMessageTextView(item)
+ when (item) {
+ is OperatorMessageItem.PlainText -> addMessageTextView(item)
+ is OperatorMessageItem.ResponseCard -> addSingleChoiceCardView(item, onOptionClickedListener)
}
updateOperatorStatusView(item)
}
private fun addSingleChoiceCardView(
- item: ResponseCardItem,
+ item: OperatorMessageItem.ResponseCard,
onOptionClickedListener: OnOptionClickedListener
) {
val singleChoiceCardView = SingleChoiceCardView(itemView.context)
@@ -101,7 +99,7 @@ internal class OperatorMessageViewHolder(
itemView.contentDescription = item.content
}
- private fun addMessageTextView(item: OperatorMessageItem) {
+ private fun addMessageTextView(item: OperatorMessageItem.PlainText) {
messageContentView.text = item.content
binding.contentLayout.addView(messageContentView)
if (!TextUtils.isEmpty(item.operatorName)) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
index 53b4f731a..0414f0bef 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
@@ -6,7 +6,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.glia.widgets.R
import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.model.history.OperatorStatusItem
+import com.glia.widgets.chat.model.OperatorStatusItem
import com.glia.widgets.databinding.ChatOperatorStatusLayoutBinding
import com.glia.widgets.di.Dependencies
import com.glia.widgets.helper.getColorCompat
@@ -72,15 +72,11 @@ internal class OperatorStatusViewHolder(
fun bind(item: OperatorStatusItem) {
chatStartingHeadingView.text = item.companyName
- when (item.status) {
- OperatorStatusItem.Status.IN_QUEUE -> applyInQueueState(item.companyName)
- OperatorStatusItem.Status.OPERATOR_CONNECTED -> applyConnectedState(
- item.operatorName,
- item.profileImgUrl
- )
-
- OperatorStatusItem.Status.JOINED -> applyJoinedState(item.operatorName, item.profileImgUrl)
- OperatorStatusItem.Status.TRANSFERRING -> applyTransferringState()
+ when (item) {
+ is OperatorStatusItem.Connected -> applyConnectedState(item.operatorName, item.profileImgUrl)
+ is OperatorStatusItem.InQueue -> applyInQueueState(item.companyName)
+ is OperatorStatusItem.Joined -> applyConnectedState(item.operatorName, item.profileImgUrl)
+ is OperatorStatusItem.Transferring -> applyTransferringState()
}
statusPictureView.isVisible = true
statusPictureView.setShowRippleAnimation(isShowStatusViewRippleAnimation(item))
@@ -90,18 +86,16 @@ internal class OperatorStatusViewHolder(
statusPictureView.showPlaceholder()
applyChatStartingViewsVisibility()
applyChatStartedViewsVisibility(false)
- itemView.contentDescription =
- itemView.resources.getString(
- R.string.glia_chat_in_queue_message_content_description,
- companyName ?: ""
- )
+ itemView.contentDescription = itemView.resources.getString(
+ R.string.glia_chat_in_queue_message_content_description,
+ companyName ?: ""
+ )
engagementStatesTheme?.queue.also(::applyEngagementState)
}
private fun applyConnectedState(operatorName: String, profileImgUrl: String?) {
- profileImgUrl?.let { statusPictureView.showProfileImage(it) }
- ?: statusPictureView.showPlaceholder()
+ profileImgUrl?.let { statusPictureView.showProfileImage(it) } ?: statusPictureView.showPlaceholder()
applyChatStartingViewsVisibility(false)
applyChatStartedViewsVisibility()
@@ -117,23 +111,6 @@ internal class OperatorStatusViewHolder(
engagementStatesTheme?.connected.also(::applyEngagementState)
}
- private fun applyJoinedState(operatorName: String, profileImgUrl: String?) {
- profileImgUrl?.let { statusPictureView.showProfileImage(it) }
- ?: statusPictureView.showPlaceholder()
- chatStartedNameView.text = operatorName
- chatStartedCaptionView.text =
- itemView.resources.getString(R.string.glia_chat_operator_has_joined, operatorName)
- itemView.contentDescription = itemView.resources.getString(
- R.string.glia_chat_operator_has_joined_content_description,
- operatorName
- )
-
- applyChatStartingViewsVisibility(false)
- applyChatStartedViewsVisibility()
-
- engagementStatesTheme?.connecting.also(::applyEngagementState)
- }
-
private fun applyTransferringState() {
statusPictureView.showPlaceholder()
applyChatStartingViewsVisibility()
@@ -167,7 +144,8 @@ internal class OperatorStatusViewHolder(
}
}
- private fun isShowStatusViewRippleAnimation(item: OperatorStatusItem) = item.status.let {
- it == OperatorStatusItem.Status.IN_QUEUE || it == OperatorStatusItem.Status.TRANSFERRING
+ private fun isShowStatusViewRippleAnimation(item: OperatorStatusItem): Boolean = when (item) {
+ is OperatorStatusItem.InQueue, is OperatorStatusItem.Transferring -> true
+ else -> false
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/VisitorMessageViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/VisitorMessageViewHolder.kt
index 9dff64b48..2b1e81d1b 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/VisitorMessageViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/VisitorMessageViewHolder.kt
@@ -4,7 +4,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.glia.widgets.R
import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.model.history.VisitorMessageItem
+import com.glia.widgets.chat.model.VisitorMessageItem
import com.glia.widgets.databinding.ChatVisitorMessageLayoutBinding
import com.glia.widgets.di.Dependencies
import com.glia.widgets.helper.getColorCompat
@@ -46,9 +46,9 @@ internal class VisitorMessageViewHolder(
fun bind(item: VisitorMessageItem) {
binding.content.text = item.message
- binding.deliveredView.isVisible = item.isShowDelivered
+ binding.deliveredView.isVisible = item.showDelivered
val contentDescription = itemView.resources.getString(
- if (item.isShowDelivered) {
+ if (item.showDelivered) {
R.string.glia_chat_visitor_message_delivered_content_description
} else {
R.string.glia_chat_visitor_message_content_description
@@ -57,4 +57,8 @@ internal class VisitorMessageViewHolder(
)
itemView.contentDescription = contentDescription
}
+
+ fun updateDelivered(delivered: Boolean) {
+ binding.deliveredView.isVisible = delivered
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/OperatorFileAttachmentViewHolder.java b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/OperatorFileAttachmentViewHolder.java
index 94626e57e..040340ecd 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/OperatorFileAttachmentViewHolder.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/OperatorFileAttachmentViewHolder.java
@@ -11,7 +11,7 @@
import com.glia.widgets.R;
import com.glia.widgets.UiTheme;
import com.glia.widgets.chat.adapter.ChatAdapter;
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem;
+import com.glia.widgets.chat.model.OperatorAttachmentItem;
import com.glia.widgets.view.OperatorStatusView;
public class OperatorFileAttachmentViewHolder extends FileAttachmentViewHolder {
@@ -23,8 +23,8 @@ public OperatorFileAttachmentViewHolder(@NonNull View itemView, UiTheme uiTheme)
setupOperatorStatusView(uiTheme);
}
- public void bind(OperatorAttachmentItem item, ChatAdapter.OnFileItemClickListener listener) {
- super.setData(item.isFileExists, item.isDownloading, item.attachmentFile, listener);
+ public void bind(OperatorAttachmentItem.File item, ChatAdapter.OnFileItemClickListener listener) {
+ super.setData(item.isFileExists(), item.isDownloading(), item.getAttachmentFile(), listener);
updateOperatorStatusView(item);
}
@@ -33,30 +33,30 @@ private void setupOperatorStatusView(UiTheme uiTheme) {
operatorStatusView.setShowRippleAnimation(false);
}
- private void updateOperatorStatusView(OperatorAttachmentItem item) {
- operatorStatusView.setVisibility(item.showChatHead ? View.VISIBLE : View.GONE);
- if (item.operatorProfileImgUrl != null) {
- operatorStatusView.showProfileImage(item.operatorProfileImgUrl);
+ private void updateOperatorStatusView(OperatorAttachmentItem.File item) {
+ operatorStatusView.setVisibility(item.getShowChatHead() ? View.VISIBLE : View.GONE);
+ if (item.getOperatorProfileImgUrl() != null) {
+ operatorStatusView.showProfileImage(item.getOperatorProfileImgUrl());
} else {
operatorStatusView.showPlaceholder();
}
- String name = item.attachmentFile.getName();
- String byteSize = Formatter.formatFileSize(itemView.getContext(), item.attachmentFile.getSize());
+ String name = item.getAttachmentFile().getName();
+ String byteSize = Formatter.formatFileSize(itemView.getContext(), item.getAttachmentFile().getSize());
itemView.setContentDescription(itemView.getResources().getString(R.string.glia_chat_operator_file_content_description, name, byteSize));
ViewCompat.setAccessibilityDelegate(itemView, new AccessibilityDelegateCompat() {
@Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
- String actionLabel = host.getResources().getString(item.isFileExists
- ? R.string.glia_chat_attachment_open_button_label
- : R.string.glia_chat_attachment_download_button_label);
+ String actionLabel = host.getResources().getString(item.isFileExists()
+ ? R.string.glia_chat_attachment_open_button_label
+ : R.string.glia_chat_attachment_download_button_label);
AccessibilityNodeInfoCompat.AccessibilityActionCompat actionClick
- = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
- AccessibilityNodeInfoCompat.ACTION_CLICK, actionLabel);
+ = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLICK, actionLabel);
info.addAction(actionClick);
}
});
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/VisitorFileAttachmentViewHolder.java b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/VisitorFileAttachmentViewHolder.java
index aa000f977..50dd6ab50 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/VisitorFileAttachmentViewHolder.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/VisitorFileAttachmentViewHolder.java
@@ -16,7 +16,7 @@
import com.glia.widgets.R;
import com.glia.widgets.UiTheme;
import com.glia.widgets.chat.adapter.ChatAdapter;
-import com.glia.widgets.chat.model.history.VisitorAttachmentItem;
+import com.glia.widgets.chat.model.VisitorAttachmentItem;
public class VisitorFileAttachmentViewHolder extends FileAttachmentViewHolder {
private final TextView deliveredView;
@@ -27,8 +27,8 @@ public VisitorFileAttachmentViewHolder(@NonNull View itemView, UiTheme uiTheme)
setupDeliveredView(itemView.getContext(), uiTheme);
}
- public void bind(VisitorAttachmentItem item, ChatAdapter.OnFileItemClickListener listener) {
- super.setData(item.isFileExists, item.isDownloading, item.attachmentFile, listener);
+ public void bind(VisitorAttachmentItem.File item, ChatAdapter.OnFileItemClickListener listener) {
+ super.setData(item.isFileExists(), item.isDownloading(), item.getAttachmentFile(), listener);
updateDeliveredView(item);
}
@@ -41,33 +41,37 @@ private void setupDeliveredView(Context context, UiTheme uiTheme) {
}
private void updateDeliveredView(VisitorAttachmentItem item) {
- deliveredView.setVisibility(item.showDelivered ? View.VISIBLE : View.GONE);
+ deliveredView.setVisibility(item.getShowDelivered() ? View.VISIBLE : View.GONE);
setAccessibilityLabels(item);
}
private void setAccessibilityLabels(VisitorAttachmentItem item) {
- String name = item.attachmentFile.getName();
- String byteSize = Formatter.formatFileSize(itemView.getContext(), item.attachmentFile.getSize());
- itemView.setContentDescription(itemView.getResources().getString(item.showDelivered
- ? R.string.glia_chat_visitor_file_delivered_content_description
- : R.string.glia_chat_visitor_file_content_description,
- name, byteSize));
+ String name = item.getAttachmentFile().getName();
+ String byteSize = Formatter.formatFileSize(itemView.getContext(), item.getAttachmentFile().getSize());
+ itemView.setContentDescription(itemView.getResources().getString(item.getShowDelivered()
+ ? R.string.glia_chat_visitor_file_delivered_content_description
+ : R.string.glia_chat_visitor_file_content_description,
+ name, byteSize));
ViewCompat.setAccessibilityDelegate(itemView, new AccessibilityDelegateCompat() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
- String actionLabel = host.getResources().getString(item.isFileExists
- ? R.string.glia_chat_attachment_open_button_label
- : R.string.glia_chat_attachment_download_button_label);
+ String actionLabel = host.getResources().getString(item.isFileExists()
+ ? R.string.glia_chat_attachment_open_button_label
+ : R.string.glia_chat_attachment_download_button_label);
AccessibilityNodeInfoCompat.AccessibilityActionCompat actionClick
- = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
- AccessibilityNodeInfoCompat.ACTION_CLICK, actionLabel);
+ = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLICK, actionLabel);
info.addAction(actionClick);
}
});
}
+
+ public void updateDelivered(boolean delivered) {
+ deliveredView.setVisibility(delivered ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/OperatorImageAttachmentViewHolder.java b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/OperatorImageAttachmentViewHolder.java
index 808988b07..b7ce9cb8d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/OperatorImageAttachmentViewHolder.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/OperatorImageAttachmentViewHolder.java
@@ -10,7 +10,7 @@
import com.glia.widgets.R;
import com.glia.widgets.UiTheme;
import com.glia.widgets.chat.adapter.ChatAdapter;
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem;
+import com.glia.widgets.chat.model.OperatorAttachmentItem;
import com.glia.widgets.filepreview.domain.usecase.GetImageFileFromCacheUseCase;
import com.glia.widgets.filepreview.domain.usecase.GetImageFileFromDownloadsUseCase;
import com.glia.widgets.filepreview.domain.usecase.GetImageFileFromNetworkUseCase;
@@ -36,9 +36,9 @@ private void setupOperatorStatus(UiTheme uiTheme) {
operatorStatusView.setShowRippleAnimation(false);
}
- public void bind(OperatorAttachmentItem item, ChatAdapter.OnImageItemClickListener onImageItemClickListener) {
- super.bind(item.attachmentFile);
- itemView.setOnClickListener(v -> onImageItemClickListener.onImageItemClick(item.attachmentFile, v));
+ public void bind(OperatorAttachmentItem.Image item, ChatAdapter.OnImageItemClickListener onImageItemClickListener) {
+ super.bind(item.getAttachmentFile());
+ itemView.setOnClickListener(v -> onImageItemClickListener.onImageItemClick(item.getAttachmentFile(), v));
updateOperatorStatus(item);
setAccessibilityLabels();
@@ -63,9 +63,9 @@ public void onInitializeAccessibilityNodeInfo(@NonNull View host, @NonNull Acces
}
private void updateOperatorStatus(OperatorAttachmentItem item) {
- operatorStatusView.setVisibility(item.showChatHead ? View.VISIBLE : View.GONE);
- if (item.operatorProfileImgUrl != null) {
- operatorStatusView.showProfileImage(item.operatorProfileImgUrl);
+ operatorStatusView.setVisibility(item.getShowChatHead() ? View.VISIBLE : View.GONE);
+ if (item.getOperatorProfileImgUrl() != null) {
+ operatorStatusView.showProfileImage(item.getOperatorProfileImgUrl());
} else {
operatorStatusView.showPlaceholder();
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/VisitorImageAttachmentViewHolder.java b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/VisitorImageAttachmentViewHolder.java
index fe73bff6a..298d33c80 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/VisitorImageAttachmentViewHolder.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/imageattachment/VisitorImageAttachmentViewHolder.java
@@ -20,11 +20,11 @@ public class VisitorImageAttachmentViewHolder extends ImageAttachmentViewHolder
private final TextView deliveredView;
public VisitorImageAttachmentViewHolder(
- @NonNull View itemView,
- UiTheme uiTheme,
- GetImageFileFromCacheUseCase getImageFileFromCacheUseCase,
- GetImageFileFromDownloadsUseCase getImageFileFromDownloadsUseCase,
- GetImageFileFromNetworkUseCase getImageFileFromNetworkUseCase
+ @NonNull View itemView,
+ UiTheme uiTheme,
+ GetImageFileFromCacheUseCase getImageFileFromCacheUseCase,
+ GetImageFileFromDownloadsUseCase getImageFileFromDownloadsUseCase,
+ GetImageFileFromNetworkUseCase getImageFileFromNetworkUseCase
) {
super(itemView, getImageFileFromCacheUseCase, getImageFileFromDownloadsUseCase, getImageFileFromNetworkUseCase);
deliveredView = itemView.findViewById(R.id.delivered_view);
@@ -41,10 +41,10 @@ public void bind(AttachmentFile attachmentFile, boolean showDelivered) {
private void setAccessibilityLabels(boolean showDelivered) {
if (showDelivered) {
itemView.setContentDescription(itemView.getResources().getString(
- R.string.glia_chat_visitor_image_delivered_content_description));
+ R.string.glia_chat_visitor_image_delivered_content_description));
} else {
itemView.setContentDescription(itemView.getResources().getString(
- R.string.glia_chat_visitor_image_content_description));
+ R.string.glia_chat_visitor_image_content_description));
}
}
@@ -59,4 +59,8 @@ private void setupDeliveredView(Context context, UiTheme uiTheme) {
}
deliveredView.setTextColor(ContextCompat.getColor(context, uiTheme.getBaseNormalColor()));
}
+
+ public void updateDelivered(boolean delivered) {
+ deliveredView.setVisibility(delivered ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
index 6af928ac8..4c1d9ab9b 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
@@ -1,16 +1,10 @@
package com.glia.widgets.chat.controller
import android.net.Uri
-import android.text.format.DateUtils
import android.view.View
-import androidx.annotation.VisibleForTesting
import com.glia.androidsdk.GliaException
import com.glia.androidsdk.Operator
import com.glia.androidsdk.chat.AttachmentFile
-import com.glia.androidsdk.chat.Chat
-import com.glia.androidsdk.chat.ChatMessage
-import com.glia.androidsdk.chat.FilesAttachment
-import com.glia.androidsdk.chat.MessageAttachment
import com.glia.androidsdk.chat.SingleChoiceAttachment
import com.glia.androidsdk.chat.SingleChoiceOption
import com.glia.androidsdk.chat.VisitorMessage
@@ -23,41 +17,28 @@ import com.glia.androidsdk.omnicore.OmnicoreEngagement
import com.glia.androidsdk.site.SiteInfo
import com.glia.widgets.Constants
import com.glia.widgets.GliaWidgets
+import com.glia.widgets.chat.ChatManager
import com.glia.widgets.chat.ChatType
import com.glia.widgets.chat.ChatView
import com.glia.widgets.chat.ChatViewCallback
-import com.glia.widgets.chat.adapter.ChatAdapter
-import com.glia.widgets.chat.domain.AddNewMessagesDividerUseCase
-import com.glia.widgets.chat.domain.CustomCardAdapterTypeUseCase
-import com.glia.widgets.chat.domain.CustomCardShouldShowUseCase
-import com.glia.widgets.chat.domain.CustomCardTypeUseCase
-import com.glia.widgets.chat.domain.GliaLoadHistoryUseCase
-import com.glia.widgets.chat.domain.GliaOnMessageUseCase
import com.glia.widgets.chat.domain.GliaOnOperatorTypingUseCase
import com.glia.widgets.chat.domain.GliaSendMessagePreviewUseCase
import com.glia.widgets.chat.domain.GliaSendMessageUseCase
-import com.glia.widgets.chat.domain.IsEnableChatEditTextUseCase
+import com.glia.widgets.chat.domain.IsAuthenticatedUseCase
import com.glia.widgets.chat.domain.IsFromCallScreenUseCase
import com.glia.widgets.chat.domain.IsSecureConversationsChatAvailableUseCase
import com.glia.widgets.chat.domain.IsShowSendButtonUseCase
-import com.glia.widgets.chat.domain.PreEngagementMessageUseCase
import com.glia.widgets.chat.domain.SiteInfoUseCase
import com.glia.widgets.chat.domain.UpdateFromCallScreenUseCase
-import com.glia.widgets.chat.model.ChatInputMode
+import com.glia.widgets.chat.domain.gva.DetermineGvaButtonTypeUseCase
+import com.glia.widgets.chat.model.ChatItem
import com.glia.widgets.chat.model.ChatState
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.CustomCardItem
-import com.glia.widgets.chat.model.history.LinkedChatItem
-import com.glia.widgets.chat.model.history.MediaUpgradeStartedTimerItem
-import com.glia.widgets.chat.model.history.NewMessagesItem
-import com.glia.widgets.chat.model.history.OperatorAttachmentItem
-import com.glia.widgets.chat.model.history.OperatorChatItem
-import com.glia.widgets.chat.model.history.OperatorMessageItem
-import com.glia.widgets.chat.model.history.OperatorStatusItem
-import com.glia.widgets.chat.model.history.ResponseCardItem
-import com.glia.widgets.chat.model.history.SystemChatItem
-import com.glia.widgets.chat.model.history.VisitorAttachmentItem
-import com.glia.widgets.chat.model.history.VisitorMessageItem
+import com.glia.widgets.chat.model.CustomCardChatItem
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaButton
+import com.glia.widgets.chat.model.OperatorMessageItem
+import com.glia.widgets.chat.model.OperatorStatusItem
+import com.glia.widgets.chat.model.Unsent
import com.glia.widgets.core.callvisualizer.domain.IsCallVisualizerUseCase
import com.glia.widgets.core.chathead.domain.HasPendingSurveyUseCase
import com.glia.widgets.core.chathead.domain.SetPendingSurveyUsedUseCase
@@ -70,8 +51,7 @@ import com.glia.widgets.core.engagement.domain.GliaOnEngagementUseCase
import com.glia.widgets.core.engagement.domain.IsOngoingEngagementUseCase
import com.glia.widgets.core.engagement.domain.IsQueueingEngagementUseCase
import com.glia.widgets.core.engagement.domain.SetEngagementConfigUseCase
-import com.glia.widgets.core.engagement.domain.model.ChatHistoryResponse
-import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.core.engagement.domain.UpdateOperatorDefaultImageUrlUseCase
import com.glia.widgets.core.engagement.domain.model.EngagementStateEvent
import com.glia.widgets.core.engagement.domain.model.EngagementStateEventVisitor
import com.glia.widgets.core.engagement.domain.model.EngagementStateEventVisitor.OperatorVisitor
@@ -96,7 +76,6 @@ import com.glia.widgets.core.queue.domain.GliaQueueForChatEngagementUseCase
import com.glia.widgets.core.queue.domain.QueueTicketStateChangeToUnstaffedUseCase
import com.glia.widgets.core.queue.domain.exception.QueueingOngoingException
import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
-import com.glia.widgets.core.secureconversations.domain.MarkMessagesReadWithDelayUseCase
import com.glia.widgets.core.survey.OnSurveyListener
import com.glia.widgets.core.survey.domain.GliaSurveyUseCase
import com.glia.widgets.di.Dependencies
@@ -115,7 +94,6 @@ import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.Observer
-import java.util.UUID
internal class ChatController(
chatViewCallback: ChatViewCallback,
@@ -125,11 +103,9 @@ internal class ChatController(
private val dialogController: DialogController,
private val messagesNotSeenHandler: MessagesNotSeenHandler,
private val callNotificationUseCase: CallNotificationUseCase,
- private val loadHistoryUseCase: GliaLoadHistoryUseCase,
private val queueForChatEngagementUseCase: GliaQueueForChatEngagementUseCase,
private val getEngagementUseCase: GliaOnEngagementUseCase,
private val engagementEndUseCase: GliaOnEngagementEndUseCase,
- private val onMessageUseCase: GliaOnMessageUseCase,
private val onOperatorTypingUseCase: GliaOnOperatorTypingUseCase,
private val sendMessagePreviewUseCase: GliaSendMessagePreviewUseCase,
private val sendMessageUseCase: GliaSendMessageUseCase,
@@ -145,15 +121,11 @@ internal class ChatController(
private val isShowSendButtonUseCase: IsShowSendButtonUseCase,
private val isShowOverlayPermissionRequestDialogUseCase: IsShowOverlayPermissionRequestDialogUseCase,
private val downloadFileUseCase: DownloadFileUseCase,
- private val isEnableChatEditTextUseCase: IsEnableChatEditTextUseCase,
private val siteInfoUseCase: SiteInfoUseCase,
private val surveyUseCase: GliaSurveyUseCase,
private val getGliaEngagementStateFlowableUseCase: GetEngagementStateFlowableUseCase,
private val isFromCallScreenUseCase: IsFromCallScreenUseCase,
private val updateFromCallScreenUseCase: UpdateFromCallScreenUseCase,
- private val customCardAdapterTypeUseCase: CustomCardAdapterTypeUseCase,
- private val customCardTypeUseCase: CustomCardTypeUseCase,
- private val customCardShouldShowUseCase: CustomCardShouldShowUseCase,
private val ticketStateChangeToUnstaffedUseCase: QueueTicketStateChangeToUnstaffedUseCase,
private val isQueueingEngagementUseCase: IsQueueingEngagementUseCase,
private val addMediaUpgradeCallbackUseCase: AddMediaUpgradeOfferCallbackUseCase,
@@ -162,21 +134,21 @@ internal class ChatController(
private val isOngoingEngagementUseCase: IsOngoingEngagementUseCase,
private val engagementConfigUseCase: SetEngagementConfigUseCase,
private val isSecureEngagementAvailableUseCase: IsSecureConversationsChatAvailableUseCase,
- private val markMessagesReadWithDelayUseCase: MarkMessagesReadWithDelayUseCase,
private val hasPendingSurveyUseCase: HasPendingSurveyUseCase,
private val setPendingSurveyUsedUseCase: SetPendingSurveyUsedUseCase,
private val isCallVisualizerUseCase: IsCallVisualizerUseCase,
- private val preEngagementMessageUseCase: PreEngagementMessageUseCase,
- private val addNewMessagesDividerUseCase: AddNewMessagesDividerUseCase,
private val isFileReadyForPreviewUseCase: IsFileReadyForPreviewUseCase,
- private val acceptMediaUpgradeOfferUseCase: AcceptMediaUpgradeOfferUseCase
+ private val acceptMediaUpgradeOfferUseCase: AcceptMediaUpgradeOfferUseCase,
+ private val determineGvaButtonTypeUseCase: DetermineGvaButtonTypeUseCase,
+ private val isAuthenticatedUseCase: IsAuthenticatedUseCase,
+ private val updateOperatorDefaultImageUrlUseCase: UpdateOperatorDefaultImageUrlUseCase,
+ private val chatManager: ChatManager
) : GliaOnEngagementUseCase.Listener, GliaOnEngagementEndUseCase.Listener, OnSurveyListener {
private var backClickedListener: ChatView.OnBackClickedListener? = null
private var viewCallback: ChatViewCallback? = null
private var mediaUpgradeOfferRepositoryCallback: MediaUpgradeOfferRepositoryCallback? = null
private var timerStatusListener: FormattedTimerStatusListener? = null
private var engagementStateEventDisposable: Disposable? = null
- private var unengagementMessagesDisposable: Disposable? = null
private val disposable = CompositeDisposable()
private val operatorMediaStateListener =
@@ -185,28 +157,22 @@ internal class ChatController(
private val sendMessageCallback: GliaSendMessageUseCase.Listener =
object : GliaSendMessageUseCase.Listener {
override fun messageSent(message: VisitorMessage?) {
- onMessageSent(message)
- }
-
- override fun onCardMessageUpdated(message: ChatMessage) {
- updateCustomCard(message)
+ Logger.d(TAG, "messageSent: $message, id: ${message?.id}")
+ message?.also { chatManager.onChatAction(ChatManager.Action.MessageSent(it)) }
+ scrollChatToBottom()
}
override fun onMessageValidated() {
viewCallback?.clearMessageInput()
-
emitViewState {
- chatState
- .setLastTypedText(EMPTY_MESSAGE)
- .setShowSendButton(isShowSendButtonUseCase(EMPTY_MESSAGE))
+ chatState.setLastTypedText("").setShowSendButton(isShowSendButtonUseCase(""))
}
}
- override fun errorOperatorNotOnline(message: String) {
+ override fun errorOperatorNotOnline(message: Unsent) {
onSendMessageOperatorOffline(message)
}
- override fun errorMessageInvalid() {}
override fun error(ex: GliaException) {
onMessageSendError(ex)
}
@@ -224,8 +190,7 @@ internal class ChatController(
viewCallback?.apply {
emitUploadAttachments(getFileAttachmentsUseCase.execute())
emitViewState {
- chatState
- .setShowSendButton(isShowSendButtonUseCase(chatState.lastTypedText))
+ chatState.setShowSendButton(isShowSendButtonUseCase(chatState.lastTypedText))
.setIsAttachmentButtonEnabled(supportedFileCountCheckUseCase.execute())
}
}
@@ -246,6 +211,7 @@ internal class ChatController(
) {
val queueIds = if (queueId != null) arrayOf(queueId) else emptyArray()
engagementConfigUseCase(chatType, queueIds)
+ updateOperatorDefaultImageUrlUseCase()
if (!hasPendingSurveyUseCase.invoke()) {
ensureSecureMessagingAvailable()
@@ -254,16 +220,12 @@ internal class ChatController(
if (isSecureEngagement) {
emitViewState { chatState.setSecureMessagingState() }
}
+ chatManager.onChatAction(ChatManager.Action.ChatRestored)
return
}
- var initChatState = chatState.initChat(companyName, queueId, visitorContextAssetId)
- if (isSecureEngagement) {
- initChatState = initChatState.setSecureMessagingState()
- }
- prepareChatComponents()
- emitViewState { initChatState }
- loadChatHistory()
+ emitViewState { chatState.initChat(companyName, queueId, visitorContextAssetId) }
+ initChatManager()
}
}
@@ -314,18 +276,6 @@ internal class ChatController(
}
}
- @Synchronized
- private fun emitChatItems(callback: () -> ChatState?) {
- val state = callback() ?: return
-
- if (setState(state) && viewCallback != null) {
- Logger.d(TAG, """Emit chat items: ${state.chatItems} (State): $state""".trimIndent())
-
- viewCallback?.emitItems(state.chatItems)
- viewCallback?.emitUploadAttachments(getFileAttachmentsUseCase.execute())
- }
- }
-
fun onDestroy(retain: Boolean) {
Logger.d(TAG, "onDestroy, retain:$retain")
dialogController.dismissMessageCenterUnavailableDialog()
@@ -344,10 +294,10 @@ internal class ChatController(
minimizeHandler.clear()
getEngagementUseCase.unregisterListener(this)
engagementEndUseCase.unregisterListener(this)
- onMessageUseCase.unregisterListener()
onOperatorTypingUseCase.unregisterListener()
removeFileAttachmentObserverUseCase.execute(fileAttachmentObserver)
shouldHandleEndedEngagement = false
+ chatManager.reset()
}
}
@@ -379,6 +329,7 @@ internal class ChatController(
Logger.d(TAG, "Send MESSAGE: $message")
clearMessagePreview()
sendMessageUseCase.execute(message, sendMessageCallback)
+ addQuickReplyButtons(emptyList())
}
private fun sendMessagePreview(message: String) {
@@ -389,111 +340,7 @@ internal class ChatController(
private fun clearMessagePreview() {
// An empty string has to be sent to clear the message preview.
- sendMessagePreview(EMPTY_MESSAGE)
- }
-
- private fun subscribeToMessages() {
- disposable.add(
- onMessageUseCase.execute()
- .doOnNext { onMessage(it) }
- .subscribe()
- )
- }
-
- private fun subscribeToPreEngagementMessage() {
- val subscribe = preEngagementMessageUseCase.execute()
- .doOnNext { onPreEngagementMessage(it) }
- .subscribe()
- disposable.add(subscribe)
- unengagementMessagesDisposable = subscribe
- }
-
- private fun onPreEngagementMessage(messageInternal: ChatMessageInternal) {
- emitChatItems {
- val message = messageInternal.chatMessage
- if (message.senderType == Chat.Participant.VISITOR && message.attachment != null &&
- !isNewMessage(chatState.chatItems, message)
- ) {
- val items: MutableList = chatState.chatItems.toMutableList()
- val currentMessage =
- items.first { (it as? LinkedChatItem)?.messageId == message.id }
- val currentMessageIndex = items.indexOf(currentMessage)
- items.removeAll { (it as? VisitorAttachmentItem)?.messageId == message.id }
- addVisitorAttachmentItemsToChatItems(items, message, currentMessageIndex + 1)
- return@emitChatItems chatState.changeItems(items)
- } else {
- onMessage(messageInternal)
- }
- return@emitChatItems null
- }
- }
-
- private fun onMessage(messageInternal: ChatMessageInternal) {
- emitChatItems {
- val message = messageInternal.chatMessage
- if (!isNewMessage(chatState.chatItems, message)) {
- return@emitChatItems null
- }
- val isUnsentMessage =
- chatState.unsentMessages.isNotEmpty() && chatState.unsentMessages[0].message == message.content
- Logger.d(
- TAG,
- "onMessage: ${message.content}, id: ${message.id}, isUnsentMessage: $isUnsentMessage"
- )
- if (isUnsentMessage) {
- // emitting state because there is no need to change recyclerview items here
- emitViewState {
- val unsentMessages: MutableList =
- chatState.unsentMessages.toMutableList()
- val currentMessage = unsentMessages[0]
- unsentMessages.remove(currentMessage)
- val currentChatItems: MutableList =
- chatState.chatItems.toMutableList()
- val currentMessageIndex = currentChatItems.indexOf(currentMessage)
- currentChatItems.remove(currentMessage)
- currentChatItems.add(
- currentMessageIndex,
- VisitorMessageItem.asNewMessage(message)
- )
-
- return@emitViewState chatState.changeItems(currentChatItems)
- .changeUnsentMessages(unsentMessages)
- }
- if (chatState.unsentMessages.isNotEmpty()) {
- sendMessageUseCase.execute(
- chatState.unsentMessages[0].message,
- sendMessageCallback
- )
- }
- return@emitChatItems null
- }
-
- val items: MutableList = chatState.chatItems.toMutableList()
- appendMessageItem(items, messageInternal)
-
- return@emitChatItems chatState.changeItems(items)
- }
- }
-
- private fun onMessageSent(message: VisitorMessage?) {
- if (message != null) {
- Logger.d(TAG, "messageSent: $message, id: ${message.id}")
- emitChatItems {
- val currentChatItems: MutableList = chatState.chatItems.toMutableList()
- if (isQueueingOrOngoingEngagement) {
- changeDeliveredIndex(currentChatItems, message)
- } else if (isSecureEngagementUseCase() && isNewMessage(currentChatItems, message)) {
- appendSentMessage(currentChatItems, message)
- }
-
- // chat input mode has to be set to enable after a message is sent
- if (isEnableChatEditTextUseCase(currentChatItems)) {
- emitViewState { chatState.chatInputModeChanged(ChatInputMode.ENABLED) }
- }
-
- return@emitChatItems chatState.changeItems(currentChatItems)
- }
- }
+ sendMessagePreview("")
}
private fun onMessageSendError(exception: GliaException) {
@@ -501,28 +348,17 @@ internal class ChatController(
error(exception)
}
- private fun onSendMessageOperatorOffline(message: String) {
+ private fun onSendMessageOperatorOffline(message: Unsent) {
appendUnsentMessage(message)
if (!chatState.engagementRequested) {
queueForEngagement()
}
}
- private fun appendUnsentMessage(message: String) {
+ private fun appendUnsentMessage(message: Unsent) {
Logger.d(TAG, "appendUnsentMessage: $message")
- emitChatItems {
- val unsentMessages: MutableList =
- chatState.unsentMessages.toMutableList()
- val unsentItem = VisitorMessageItem.asUnsentItem(message)
- unsentMessages.add(unsentItem)
- val currentChatItems: MutableList = chatState.chatItems.toMutableList()
- currentChatItems.add(unsentItem)
- emitViewState { chatState.changeUnsentMessages(unsentMessages) }
-
- updateQueueing(currentChatItems)
-
- return@emitChatItems chatState.changeItems(currentChatItems)
- }
+ chatManager.onChatAction(ChatManager.Action.UnsentMessageReceived(message))
+ scrollChatToBottom()
}
private fun onOperatorTyping(isOperatorTyping: Boolean) {
@@ -551,7 +387,7 @@ internal class ChatController(
viewCallback?.backToCall()
} else {
backClickedListener?.onBackClicked()
- Dependencies.getControllerFactory().destroyChatController()
+ onDestroy(isQueueingOrOngoingEngagement || isAuthenticatedUseCase())
Dependencies.getControllerFactory().destroyCallController()
}
updateFromCallScreenUseCase.updateFromCallScreen(false)
@@ -605,13 +441,10 @@ internal class ChatController(
Logger.d(TAG, "setViewCallback")
viewCallback = chatViewCallback
viewCallback?.emitState(chatState)
- viewCallback?.emitItems(chatState.chatItems)
viewCallback?.emitUploadAttachments(getFileAttachmentsUseCase.execute())
// always start in bottom
- emitViewState {
- chatState.isInBottomChanged(true).changeVisibility(true)
- }
+ emitViewState { chatState.isInBottomChanged(true).changeVisibility(true) }
viewCallback?.scrollToBottomImmediate()
chatState.pendingNavigationType?.also { viewCallback?.navigateToCall(it) }
@@ -681,7 +514,16 @@ internal class ChatController(
visitor.visit(engagementState)
)
- EngagementStateEvent.Type.ENGAGEMENT_ENDED -> {}
+ EngagementStateEvent.Type.ENGAGEMENT_ENDED -> {
+ Logger.d(TAG, "Engagement Ended")
+ if (!isOngoingEngagementUseCase.invoke()) {
+ dialogController.dismissDialogs()
+ }
+ }
+
+ EngagementStateEvent.Type.NO_ENGAGEMENT -> {
+ Logger.d(TAG, "NoEngagement")
+ }
}
}
@@ -698,16 +540,8 @@ internal class ChatController(
}
private fun onTransferring() {
- emitChatItems {
- val items: MutableList = chatState.chatItems.toMutableList()
- if (chatState.operatorStatusItem != null) {
- items.remove(chatState.operatorStatusItem)
- }
- items.add(OperatorStatusItem.TransferringStatusItem())
- emitViewState { chatState.transferring() }
-
- return@emitChatItems chatState.changeItems(items)
- }
+ emitViewState { chatState.transferring() }
+ chatManager.onChatAction(ChatManager.Action.Transferring)
}
fun overlayPermissionsDialogDismissed() {
@@ -736,6 +570,10 @@ internal class ChatController(
return true
}
+ private fun error(error: Throwable?) {
+ error?.also { error(it.toString()) }
+ }
+
private fun error(error: String) {
Logger.e(TAG, error)
dialogController.showUnexpectedErrorDialog()
@@ -745,16 +583,13 @@ internal class ChatController(
private fun initMediaUpgradeCallback() {
mediaUpgradeOfferRepositoryCallback = object : MediaUpgradeOfferRepositoryCallback {
override fun newOffer(offer: MediaUpgradeOffer) {
- if (isChatViewPaused) return
when {
+ isChatViewPaused -> return
offer.video == MediaDirection.NONE && offer.audio == MediaDirection.TWO_WAY -> {
// audio call
Logger.d(TAG, "audioUpgradeRequested")
if (chatState.isOperatorOnline) {
- dialogController.showUpgradeAudioDialog(
- offer,
- chatState.formattedOperatorName
- )
+ dialogController.showUpgradeAudioDialog(offer, chatState.formattedOperatorName)
}
}
@@ -762,20 +597,14 @@ internal class ChatController(
// video call
Logger.d(TAG, "2 way videoUpgradeRequested")
if (chatState.isOperatorOnline) {
- dialogController.showUpgradeVideoDialog2Way(
- offer,
- chatState.formattedOperatorName
- )
+ dialogController.showUpgradeVideoDialog2Way(offer, chatState.formattedOperatorName)
}
}
offer.video == MediaDirection.ONE_WAY -> {
Logger.d(TAG, "1 way videoUpgradeRequested")
if (chatState.isOperatorOnline) {
- dialogController.showUpgradeVideoDialog1Way(
- offer,
- chatState.formattedOperatorName
- )
+ dialogController.showUpgradeVideoDialog1Way(offer, chatState.formattedOperatorName)
}
}
}
@@ -811,25 +640,14 @@ internal class ChatController(
private fun viewInitQueueing() {
Logger.d(TAG, "viewInitQueueing")
- emitChatItems {
- val items: MutableList = chatState.chatItems.toMutableList()
- if (chatState.operatorStatusItem != null) {
- items.remove(chatState.operatorStatusItem)
- }
- val operatorStatusItem = OperatorStatusItem.QueueingStatusItem(chatState.companyName)
- items.add(operatorStatusItem)
- emitViewState { chatState.queueingStarted(operatorStatusItem) }
-
- return@emitChatItems chatState.changeItems(items)
- }
+ chatManager.onChatAction(ChatManager.Action.QueuingStarted(chatState.companyName.orEmpty()))
+ emitViewState { chatState.queueingStarted() }
}
private fun updateQueueing(items: MutableList) {
- if (chatState.operatorStatusItem?.status == OperatorStatusItem.Status.IN_QUEUE) {
- items.remove(chatState.operatorStatusItem)
- items.add(
- OperatorStatusItem.QueueingStatusItem(chatState.companyName)
- )
+ (chatState.operatorStatusItem as? OperatorStatusItem.InQueue)?.also {
+ items.remove(it)
+ items.add(OperatorStatusItem.InQueue(chatState.companyName))
}
}
@@ -845,59 +663,29 @@ internal class ChatController(
}
private fun operatorConnected(formattedOperatorName: String, profileImgUrl: String?) {
- emitChatItems {
- val items: MutableList = chatState.chatItems.toMutableList()
- if (chatState.operatorStatusItem != null) {
- // remove previous operator status item
- val operatorStatusItemIndex = items.indexOf(chatState.operatorStatusItem)
- Logger.d(
- TAG,
- "operatorStatusItemIndex: " + operatorStatusItemIndex + ", size: " + items.size
- )
- items.remove(chatState.operatorStatusItem)
- items.add(
- operatorStatusItemIndex,
- OperatorStatusItem.OperatorFoundStatusItem(
- chatState.companyName,
- formattedOperatorName,
- profileImgUrl
- )
- )
- } else {
- items.add(
- OperatorStatusItem.OperatorFoundStatusItem(
- chatState.companyName,
- formattedOperatorName,
- profileImgUrl
- )
- )
- }
- emitViewState {
- chatState
- .operatorConnected(formattedOperatorName, profileImgUrl)
- .setLiveChatState()
- }
-
- return@emitChatItems chatState.changeItems(items)
- }
+ chatManager.onChatAction(
+ ChatManager.Action.OperatorConnected(
+ chatState.companyName.orEmpty(),
+ formattedOperatorName,
+ profileImgUrl
+ )
+ )
+ emitViewState { chatState.operatorConnected(formattedOperatorName, profileImgUrl).setLiveChatState() }
}
private fun operatorChanged(formattedOperatorName: String, profileImgUrl: String?) {
- emitChatItems {
- val items: MutableList = chatState.chatItems.toMutableList()
- val operatorStatusItem = OperatorStatusItem.OperatorJoinedStatusItem(
- chatState.companyName,
+ chatManager.onChatAction(
+ ChatManager.Action.OperatorJoined(
+ chatState.companyName.orEmpty(),
formattedOperatorName,
profileImgUrl
)
- items.add(operatorStatusItem)
-
- return@emitChatItems chatState.changeItems(items)
- }
+ )
emitViewState { chatState.operatorConnected(formattedOperatorName, profileImgUrl) }
}
private fun stop() {
+ chatManager.reset()
Logger.d(TAG, "Stop, engagement ended")
disposable.add(
cancelQueueTicketUseCase.execute()
@@ -910,386 +698,13 @@ internal class ChatController(
emitViewState { chatState.stop() }
}
- private fun appendHistoryChatItem(
- currentChatItems: MutableList,
- chatMessageInternal: ChatMessageInternal,
- isLastItem: Boolean
- ) {
- val message = chatMessageInternal.chatMessage
- when (message.senderType) {
- Chat.Participant.VISITOR -> {
- appendHistoryMessage(currentChatItems, message)
- addVisitorAttachmentItemsToChatItems(currentChatItems, message)
- }
-
- Chat.Participant.OPERATOR -> {
- appendOperatorMessage(currentChatItems, chatMessageInternal, isLastItem)
- }
-
- Chat.Participant.SYSTEM -> {
- appendSystemMessage(currentChatItems, chatMessageInternal)
- }
-
- Chat.Participant.UNKNOWN -> Logger.d(TAG, "Unknown type `chat item` received: $message")
- }
- }
-
- private fun appendHistoryMessage(
- currentChatItems: MutableList,
- message: ChatMessage
- ) {
- if (message.content.isNotEmpty()) {
- currentChatItems.add(VisitorMessageItem.asHistoryItem(message))
- }
- }
-
- private fun appendMessageItem(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal
- ) {
- val message = messageInternal.chatMessage
- when (message.senderType) {
- Chat.Participant.VISITOR -> {
- appendSentMessage(currentChatItems, message)
- addVisitorAttachmentItemsToChatItems(currentChatItems, message)
- }
-
- Chat.Participant.OPERATOR -> {
- onOperatorMessageReceived(currentChatItems, messageInternal)
- }
-
- Chat.Participant.SYSTEM -> {
- onSystemMessageReceived(currentChatItems, messageInternal)
- }
-
- Chat.Participant.UNKNOWN -> Logger.d(TAG, "Unknown type `chat item` received: $message")
- }
- }
-
- private fun onOperatorMessageReceived(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal
- ) {
- appendOperatorMessage(currentChatItems, messageInternal, true)
- appendMessagesNotSeen()
- }
-
- private fun onSystemMessageReceived(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal
- ) {
- appendSystemMessage(currentChatItems, messageInternal)
- appendMessagesNotSeen()
- }
-
- private fun addVisitorAttachmentItemsToChatItems(
- currentChatItems: MutableList,
- chatMessage: ChatMessage,
- index: Int? = null
- ) {
- val attachment = chatMessage.attachment
- if (attachment is FilesAttachment) {
- val visitorAttachmentItems = attachment.files.map {
- VisitorAttachmentItem.fromAttachmentFile(
- chatMessage.id,
- chatMessage.timestamp,
- it
- )
- }
- if (index != null) {
- currentChatItems.addAll(index, visitorAttachmentItems)
- } else {
- currentChatItems.addAll(visitorAttachmentItems)
- }
- }
- }
-
- private fun appendSentMessage(items: MutableList, message: ChatMessage) {
- if (message.content.isNotEmpty()) {
- items.add(VisitorMessageItem.asNewMessage(message))
- }
- }
-
- private fun appendMessagesNotSeen() {
- emitViewState {
- chatState.messagesNotSeenChanged(
- if (chatState.isChatInBottom) 0 else chatState.messagesNotSeen + 1
- )
- }
- }
-
private fun initGliaEngagementObserving() {
getEngagementUseCase.execute(this)
engagementEndUseCase.execute(this)
}
- private fun changeDeliveredIndex(
- currentChatItems: MutableList,
- message: VisitorMessage
- ) {
- // "Delivered" status only applies to visitor messages
- if (message.senderType != Chat.Participant.VISITOR) return
- val messageId = message.id
- var foundDelivered = false
- for (i in currentChatItems.indices.reversed()) {
- val currentChatItem = currentChatItems[i]
- if (currentChatItem is VisitorMessageItem) {
- val itemId = currentChatItem.id
- when {
- itemId == VisitorMessageItem.HISTORY_ID -> {
- // we reached the history items no point in going searching further
- break
- }
-
- !foundDelivered && itemId == messageId -> {
- foundDelivered = true
- currentChatItems[i] = VisitorMessageItem.editDeliveredStatus(
- currentChatItem,
- true
- )
- }
-
- currentChatItem.isShowDelivered -> {
- currentChatItems[i] = VisitorMessageItem.editDeliveredStatus(
- currentChatItem,
- false
- )
- }
- }
- } else if (currentChatItem is VisitorAttachmentItem) {
- if (!foundDelivered && currentChatItem.id == messageId) {
- foundDelivered = true
- setDelivered(currentChatItems, i, currentChatItem, true)
- } else if (currentChatItem.showDelivered) {
- setDelivered(currentChatItems, i, currentChatItem, false)
- }
- }
- }
- }
-
- private fun setDelivered(
- currentChatItems: MutableList,
- i: Int,
- item: VisitorAttachmentItem,
- delivered: Boolean
- ) {
- currentChatItems[i] = VisitorAttachmentItem.editDeliveredStatus(item, delivered)
- }
-
- private fun appendSystemMessage(
- currentChatItems: MutableList,
- chatMessageInternal: ChatMessageInternal
- ) {
- chatMessageInternal.chatMessage.apply {
- currentChatItems += SystemChatItem(id, timestamp, content)
- }
- }
-
- private fun appendOperatorMessage(
- currentChatItems: MutableList,
- chatMessageInternal: ChatMessageInternal,
- isLastItem: Boolean
- ) {
- setLastOperatorItemChatHeadVisibility(
- currentChatItems,
- isOperatorChanged(currentChatItems, chatMessageInternal)
- )
- appendOperatorOrCustomCardItem(currentChatItems, chatMessageInternal, isLastItem)
- appendOperatorAttachmentItems(currentChatItems, chatMessageInternal)
- setLastOperatorItemChatHeadVisibility(currentChatItems, true)
- }
-
- private fun isOperatorChanged(
- currentChatItems: List,
- chatMessageInternal: ChatMessageInternal
- ): Boolean {
- if (currentChatItems.isEmpty()) return false
- val lastItem = currentChatItems.last()
- if (lastItem is OperatorChatItem) {
- return !chatMessageInternal
- .operatorId
- .filter { it == lastItem.operatorId }
- .isPresent
- }
- return false
- }
-
- private fun setLastOperatorItemChatHeadVisibility(
- currentChatItems: MutableList,
- showChatHead: Boolean
- ) {
- if (currentChatItems.isNotEmpty()) {
- when (val lastItem = currentChatItems.last()) {
- is ResponseCardItem -> {
- currentChatItems.remove(lastItem)
- currentChatItems.add(
- ResponseCardItem(
- lastItem.id,
- lastItem.operatorName,
- lastItem.operatorProfileImgUrl,
- showChatHead,
- lastItem.content,
- lastItem.operatorId,
- lastItem.timestamp,
- lastItem.singleChoiceOptions,
- lastItem.choiceCardImageUrl
- )
- )
- }
-
- is OperatorMessageItem -> {
- currentChatItems.remove(lastItem)
- currentChatItems.add(
- OperatorMessageItem(
- lastItem.id,
- lastItem.operatorName,
- lastItem.operatorProfileImgUrl,
- showChatHead,
- lastItem.content,
- lastItem.operatorId,
- lastItem.timestamp
- )
- )
- }
-
- is OperatorAttachmentItem -> {
- currentChatItems.remove(lastItem)
- currentChatItems.add(
- OperatorAttachmentItem(
- lastItem.id,
- lastItem.viewType,
- showChatHead,
- lastItem.attachmentFile,
- lastItem.operatorProfileImgUrl,
- false,
- false,
- lastItem.operatorId,
- lastItem.messageId,
- lastItem.timestamp
- )
- )
- }
-
- is CustomCardItem -> {
- currentChatItems.remove(lastItem)
- currentChatItems.add(
- CustomCardItem(
- lastItem.message,
- lastItem.viewType
- )
- )
- }
- }
- }
- }
-
- private fun appendOperatorAttachmentItems(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal
- ) {
- val message = messageInternal.chatMessage
- val attachment = message.attachment
- if (attachment is FilesAttachment) {
- val files = attachment.files
- for (file in files) {
- val viewType: Int = if (file.contentType.startsWith("image")) {
- ChatAdapter.OPERATOR_IMAGE_VIEW_TYPE
- } else {
- ChatAdapter.OPERATOR_FILE_VIEW_TYPE
- }
- currentChatItems.add(
- OperatorAttachmentItem(
- message.id,
- viewType,
- false,
- file,
- messageInternal.operatorImageUrl.orElse(chatState.operatorProfileImgUrl),
- false,
- false,
- messageInternal.operatorId.orElse(UUID.randomUUID().toString()),
- message.id,
- message.timestamp
- )
- )
- }
- }
- }
-
- private fun appendOperatorOrCustomCardItem(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal,
- isLastItem: Boolean
- ) {
- val message = messageInternal.chatMessage
- if (message.content != EMPTY_MESSAGE) {
- val viewType = customCardAdapterTypeUseCase.execute(message)
- if (viewType != null) {
- appendCustomCardItem(currentChatItems, message, viewType)
- } else {
- appendOperatorMessageItem(currentChatItems, messageInternal, isLastItem)
- }
- }
- }
-
- private fun appendCustomCardItem(
- currentChatItems: MutableList,
- message: ChatMessage,
- viewType: Int
- ) {
- val customCardType = customCardTypeUseCase.execute(viewType) ?: return
- if (customCardShouldShowUseCase.execute(message, customCardType, true)) {
- currentChatItems.add(CustomCardItem(message, viewType))
- }
- val visitorCardResponseItem = VisitorMessageItem.asCardResponseItem(message)
- if (!visitorCardResponseItem.message.isNullOrEmpty()) {
- currentChatItems.add(visitorCardResponseItem)
- }
- }
-
- private fun appendOperatorMessageItem(
- currentChatItems: MutableList,
- messageInternal: ChatMessageInternal,
- isLastItem: Boolean
- ) {
- val message = messageInternal.chatMessage
- val messageAttachment = message.attachment
- val singleChoiceAttachmentOptions = getSingleChoiceAttachmentOptions(messageAttachment)
- val operatorName = messageInternal.operatorName.orElse(chatState.formattedOperatorName)
- val operatorImage = messageInternal.operatorImageUrl.orElse(chatState.operatorProfileImgUrl)
- val operatorId = messageInternal.operatorId.orElse(UUID.randomUUID().toString())
-
- val item = if (singleChoiceAttachmentOptions.isNullOrEmpty() || !isLastItem) {
- OperatorMessageItem(
- message.id,
- operatorName,
- operatorImage,
- false,
- message.content,
- operatorId,
- message.timestamp
- )
- } else {
- ResponseCardItem(
- message.id,
- operatorName,
- operatorImage,
- false,
- message.content,
- operatorId,
- message.timestamp,
- singleChoiceAttachmentOptions,
- getSingleChoiceAttachmentImgUrl(messageAttachment)
- )
- }
-
- currentChatItems.add(item)
- }
-
- private fun getSingleChoiceAttachmentImgUrl(attachment: MessageAttachment?): String? =
- (attachment as? SingleChoiceAttachment)?.imageUrl?.orElse(null)
-
- private fun getSingleChoiceAttachmentOptions(attachment: MessageAttachment?): List? {
- return (attachment as? SingleChoiceAttachment)?.options?.toList()
+ private fun addQuickReplyButtons(options: List) {
+ emitViewState { chatState.copy(gvaQuickReplies = options) }
}
private fun startTimer() {
@@ -1297,148 +712,53 @@ internal class ChatController(
callTimer.startNew(Constants.CALL_TIMER_DELAY, Constants.CALL_TIMER_INTERVAL_VALUE)
}
- private fun upgradeMediaItem() {
+ private fun upgradeMediaItemToVideo() {
Logger.d(TAG, "upgradeMediaItem")
- emitChatItems {
- val newItems: MutableList = chatState.chatItems.toMutableList()
- val mediaUpgradeStartedTimerItem = MediaUpgradeStartedTimerItem(
- MediaUpgradeStartedTimerItem.Type.VIDEO,
- chatState.mediaUpgradeStartedTimerItem.time
- )
- newItems.remove(chatState.mediaUpgradeStartedTimerItem)
- newItems.add(mediaUpgradeStartedTimerItem)
-
- return@emitChatItems chatState.changeTimerItem(newItems, mediaUpgradeStartedTimerItem)
- }
+ emitViewState { chatState.upgradeMedia(true) }
+ chatManager.onChatAction(ChatManager.Action.OnMediaUpgradeToVideo)
}
private fun createNewTimerCallback() {
timerStatusListener?.also { callTimer.removeFormattedValueListener(it) }
timerStatusListener = object : FormattedTimerStatusListener {
override fun onNewFormattedTimerValue(formatedValue: String) {
- emitChatItems {
- if (chatState.isMediaUpgradeStarted) {
- val index =
- chatState.chatItems.indexOf(chatState.mediaUpgradeStartedTimerItem)
- if (index != -1) {
- val newItems: MutableList =
- chatState.chatItems.toMutableList()
- val type = chatState.mediaUpgradeStartedTimerItem.type
- newItems.removeAt(index)
- val mediaUpgradeStartedTimerItem =
- MediaUpgradeStartedTimerItem(type, formatedValue)
- newItems.add(index, mediaUpgradeStartedTimerItem)
-
- return@emitChatItems chatState.changeTimerItem(
- newItems,
- mediaUpgradeStartedTimerItem
- )
- }
- }
- return@emitChatItems null
+ if (chatState.isMediaUpgradeStarted) {
+ chatManager.onChatAction(
+ ChatManager.Action.OnMediaUpgradeTimerUpdated(
+ formatedValue
+ )
+ )
}
}
override fun onFormattedTimerCancelled() {
- if (chatState.isMediaUpgradeStarted &&
- chatState.chatItems.contains(chatState.mediaUpgradeStartedTimerItem)
- ) {
- emitChatItems {
- val newItems: MutableList = chatState.chatItems.toMutableList()
- newItems.remove(chatState.mediaUpgradeStartedTimerItem)
-
- return@emitChatItems chatState.changeTimerItem(newItems, null)
- }
+ if (chatState.isMediaUpgradeStarted) {
+ emitViewState { chatState.upgradeMedia(null) }
+ chatManager.onChatAction(ChatManager.Action.OnMediaUpgradeCanceled)
}
}
}
}
fun singleChoiceOptionClicked(
- item: ResponseCardItem,
+ item: OperatorMessageItem.ResponseCard,
selectedOption: SingleChoiceOption
) {
Logger.d(TAG, "singleChoiceOptionClicked, id: ${item.id}")
sendMessageUseCase.execute(selectedOption.asSingleChoiceResponse(), sendMessageCallback)
- val choiceCardItemWithSelected = OperatorMessageItem(
- item.id,
- item.operatorName,
- item.operatorProfileImgUrl,
- item.showChatHead,
- item.content,
- item.operatorId,
- item.timestamp
- )
- emitChatItems {
- val modifiedItems: MutableList = chatState.chatItems.toMutableList()
- val indexInList = modifiedItems.indexOf(item)
- modifiedItems.remove(item)
- if (indexInList >= 0) {
- modifiedItems.add(indexInList, choiceCardItemWithSelected)
- } else {
- Logger.e(TAG, "singleChoiceOptionClicked, ResponseCardItem is not in the list!")
- }
-
- return@emitChatItems chatState.changeItems(modifiedItems)
- }
- }
-
- fun sendCustomCardResponse(messageId: String, text: String, value: String) {
- emitChatItems {
- chatState.chatItems
- .firstOrNull { messageId == it.id }
- ?.let { it as CustomCardItem }
- ?.also {
- sendMessageUseCase.execute(it.message, text, value, sendMessageCallback)
-
- val customCardType = customCardTypeUseCase.execute(it.viewType) ?: return@also
- val currentMessage = it.message
- val showCustomCard = customCardShouldShowUseCase.execute(
- currentMessage,
- customCardType,
- false
- )
- if (!showCustomCard) {
- val chatItems: MutableList = chatState.chatItems.toMutableList()
-
- // If the card should be hidden after the response, we remove it from the item list.
- chatItems.remove(it)
- return@emitChatItems chatState.changeItems(chatItems)
- }
- return@emitChatItems null
- } ?: run {
- sendMessageUseCase.execute(null, text, value, sendMessageCallback)
- }
- return@emitChatItems null
- }
+ chatManager.onChatAction(ChatManager.Action.ResponseCardClicked(item))
}
- private fun updateCustomCard(message: ChatMessage) {
- chatState.chatItems
- .firstOrNull { message.id == it.id }
- ?.let { it as CustomCardItem }
- ?.also {
- emitChatItems {
- val chatItems: MutableList = chatState.chatItems.toMutableList()
- updateCustomCardSelectedOption(it, message, chatItems)
+ fun sendCustomCardResponse(customCard: CustomCardChatItem, text: String, value: String) {
+ val attachment = SingleChoiceAttachment.from(value, text)
+ sendMessageUseCase.execute(attachment, sendMessageCallback)
- return@emitChatItems chatState.changeItems(chatItems)
- }
- }
+ chatManager.onChatAction(ChatManager.Action.CustomCardClicked(customCard, attachment))
}
- private fun updateCustomCardSelectedOption(
- currentCustomCardItem: CustomCardItem,
- updatedMessage: ChatMessage,
- chatItems: MutableList
- ) {
- val updatedCustomCardItem = CustomCardItem(
- updatedMessage,
- currentCustomCardItem.viewType
- )
- val indexInList = chatItems.indexOf(currentCustomCardItem)
- chatItems.removeAt(indexInList)
- chatItems.add(indexInList, updatedCustomCardItem)
+ private fun sendGvaResponse(singleChoiceAttachment: SingleChoiceAttachment) {
+ addQuickReplyButtons(emptyList())
+ sendMessageUseCase.execute(singleChoiceAttachment, sendMessageCallback)
}
fun onRecyclerviewPositionChanged(isBottom: Boolean) {
@@ -1456,139 +776,61 @@ internal class ChatController(
viewCallback?.smoothScrollToBottom()
}
- private fun loadChatHistory() {
- unengagementMessagesDisposable?.dispose()
- val historyDisposable = loadHistoryUseCase()
- .subscribe({ historyLoaded(it) }, { error(it) })
- disposable.add(historyDisposable)
- }
-
- @Synchronized
- private fun historyLoaded(historyResponse: ChatHistoryResponse) {
- Logger.d(TAG, "historyLoaded")
- val (messages, newMessagesCount) = historyResponse
- val currentItems: MutableList = chatState.chatItems.toMutableList()
- val newItems = removeDuplicates(currentItems, messages)
-
- when {
- !newItems.isNullOrEmpty() -> submitHistoryItems(
- newItems,
- currentItems,
- newMessagesCount
- )
-
- !chatState.engagementRequested && !isSecureEngagement -> queueForEngagement()
- else -> Logger.d(TAG, "Opened empty Secure Conversations chat")
- }
-
- initGliaEngagementObserving()
- subscribeToPreEngagementMessage()
- }
+ init {
+ Logger.d(TAG, "constructor")
- private fun submitHistoryItems(
- newItems: List,
- currentItems: MutableList,
- newMessagesCount: Int
- ) {
- newItems.forEachIndexed { index, message ->
- appendHistoryChatItem(currentItems, message, index == newItems.lastIndex)
- }
+ // viewCallback is accessed from multiple threads
+ // and must be protected from race condition
+ synchronized(this) { viewCallback = chatViewCallback }
- if (isSecureEngagementUseCase() && !isQueueingOrOngoingEngagement) {
- emitChatTranscriptItems(currentItems, newMessagesCount)
- } else {
- emitChatItems { chatState.historyLoaded(currentItems) }
- }
+ chatState = ChatState()
}
- @VisibleForTesting
- fun emitChatTranscriptItems(
- items: MutableList,
- newMessagesCount: Int
- ) {
- if (addNewMessagesDividerUseCase(items, newMessagesCount)) {
- emitChatItems { chatState.changeItems(items) }
- markMessagesReadWithDelay()
- } else {
- emitChatItems { chatState.changeItems(items) }
- }
+ override fun newEngagementLoaded(engagement: OmnicoreEngagement) {
+ Logger.d(TAG, "newEngagementLoaded")
+ onOperatorTypingUseCase.execute { onOperatorTyping(it) }
+ addOperatorMediaStateListenerUseCase.execute(operatorMediaStateListener)
+ mediaUpgradeOfferRepository.startListening()
+ emitViewState { chatState.engagementStarted() }
+ chatManager.reloadHistoryIfNeeded()
}
- private fun markMessagesReadWithDelay() {
- disposable.add(
- markMessagesReadWithDelayUseCase().subscribe({
- removeNewMessagesDivider()
- }, {
- Logger.e(TAG, "Marking messages read failed", it)
- })
- )
+ private fun initChatManager() {
+ chatManager.initialize(::onHistoryLoaded, ::addQuickReplyButtons, ::updateUnSeenMessagesCount)
+ .subscribe(::emitItems, ::error)
+ .also(disposable::add)
}
- private fun removeNewMessagesDivider() {
- emitChatItems { chatState.run { changeItems(chatItems - NewMessagesItem) } }
+ private fun updateUnSeenMessagesCount(count: Int) {
+ emitViewState {
+ val notSeenCount = chatState.messagesNotSeen
+ chatState.messagesNotSeenChanged(if (chatState.isChatInBottom) 0 else notSeenCount + count)
+ }
}
- init {
- Logger.d(TAG, "constructor")
+ private fun onHistoryLoaded(hasHistory: Boolean) {
+ Logger.d(TAG, "historyLoaded")
- // viewCallback is accessed from multiple threads
- // and must be protected from race condition
- synchronized(this) { viewCallback = chatViewCallback }
+ if (!hasHistory) {
+ if (!chatState.engagementRequested && !isSecureEngagement) {
+ queueForEngagement()
+ } else {
+ Logger.d(TAG, "Opened empty Secure Conversations chat")
+ }
+ }
- chatState = ChatState.Builder()
- .setFormattedOperatorName(null)
- .setCompanyName(null)
- .setQueueId(null)
- .setVisitorContextAssetId(null)
- .setIsVisible(false)
- .setIntegratorChatStarted(false)
- .setChatItems(ArrayList())
- .setLastTypedText(EMPTY_MESSAGE)
- .setChatInputMode(ChatInputMode.ENABLED_NO_ENGAGEMENT)
- .setIsAttachmentButtonNeeded(false)
- .setIsAttachmentAllowed(true)
- .setIsChatInBottom(true)
- .setMessagesNotSeen(0)
- .setPendingNavigationType(null)
- .setUnsentMessages(ArrayList())
- .setIsOperatorTyping(false)
- .createChatState()
- }
-
- @VisibleForTesting
- fun removeDuplicates(
- oldHistory: List?,
- newHistory: List?
- ): List? {
- return if (newHistory.isNullOrEmpty() || oldHistory.isNullOrEmpty()) {
- newHistory
+ if (isSecureEngagement) {
+ emitViewState { chatState.setSecureMessagingState() }
} else {
- newHistory.filter { isNewMessage(oldHistory, it.chatMessage) }
+ emitViewState { chatState.historyLoaded() }
}
- }
- @VisibleForTesting
- fun isNewMessage(oldHistory: List?, newMessage: ChatMessage): Boolean =
- oldHistory?.none { (it as? LinkedChatItem)?.messageId == newMessage.id } ?: true
-
- private fun error(error: Throwable?) {
- error?.also { error(it.toString()) }
+ prepareChatComponents()
+ initGliaEngagementObserving()
}
- override fun newEngagementLoaded(engagement: OmnicoreEngagement) {
- Logger.d(TAG, "newEngagementLoaded")
- subscribeToMessages()
- onOperatorTypingUseCase.execute { onOperatorTyping(it) }
- addOperatorMediaStateListenerUseCase.execute(operatorMediaStateListener)
- mediaUpgradeOfferRepository.startListening()
- if (chatState.unsentMessages.isNotEmpty()) {
- sendMessageUseCase.execute(chatState.unsentMessages[0].message, sendMessageCallback)
- Logger.d(TAG, "unsentMessage sent!")
- }
- emitViewState { chatState.engagementStarted() }
- // Loading chat history again on engagement start in case it was an-authenticated visitor that restored ongoing engagement
- // Currently there is no direct way to know if Visitor is authenticated.
- loadChatHistory()
+ private fun emitItems(items: List) {
+ viewCallback?.emitItems(items)
}
override fun engagementEnded() {
@@ -1623,7 +865,7 @@ internal class ChatController(
private fun onNewOperatorMediaState(operatorMediaState: OperatorMediaState?) {
Logger.d(TAG, "newOperatorMediaState: $operatorMediaState")
if (chatState.isAudioCallStarted && operatorMediaState?.video != null) {
- upgradeMediaItem()
+ upgradeMediaItemToVideo()
} else if (!chatState.isMediaUpgradeStarted) {
addMediaUpgradeItemToChatItems(operatorMediaState)
if (!callTimer.isRunning) {
@@ -1635,25 +877,17 @@ internal class ChatController(
}
private fun addMediaUpgradeItemToChatItems(operatorMediaState: OperatorMediaState?) {
- var type: MediaUpgradeStartedTimerItem.Type? = null
- if (operatorMediaState?.video == null && operatorMediaState?.audio != null) {
- Logger.d(TAG, "starting audio timer")
- type = MediaUpgradeStartedTimerItem.Type.AUDIO
- } else if (operatorMediaState?.video != null) {
- Logger.d(TAG, "starting video timer")
- type = MediaUpgradeStartedTimerItem.Type.VIDEO
- }
- emitChatItems {
- val newItems: MutableList = chatState.chatItems.toMutableList()
- val mediaUpgradeStartedTimerItem =
- MediaUpgradeStartedTimerItem(type, DateUtils.formatElapsedTime(0))
- newItems.add(mediaUpgradeStartedTimerItem)
+ val isVideo = when {
+ operatorMediaState?.video == null && operatorMediaState?.audio != null -> false
+ operatorMediaState?.video != null -> true
+ else -> null
+ } ?: return
- return@emitChatItems chatState.changeTimerItem(newItems, mediaUpgradeStartedTimerItem)
- }
+ emitViewState { chatState.upgradeMedia(isVideo) }
+ chatManager.onChatAction(ChatManager.Action.OnMediaUpgradeStarted(isVideo))
}
- fun notificationsDialogDismissed() {
+ fun notificationDialogDismissed() {
dialogController.dismissCurrentDialog()
}
@@ -1719,9 +953,7 @@ internal class ChatController(
downloadFileUseCase(attachmentFile)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe({ fileDownloadSuccess(attachmentFile) }) {
- fileDownloadError(attachmentFile, it)
- }
+ .subscribe({ fileDownloadSuccess(attachmentFile) }) { fileDownloadError(attachmentFile, it) }
)
}
@@ -1734,24 +966,19 @@ internal class ChatController(
}
private fun updateAllowFileSendState() {
- siteInfoUseCase.execute { siteInfo: SiteInfo?, _ ->
- onSiteInfoReceived(siteInfo)
- }
+ siteInfoUseCase.execute { siteInfo: SiteInfo?, _ -> onSiteInfoReceived(siteInfo) }
}
private fun onSiteInfoReceived(siteInfo: SiteInfo?) {
emitViewState {
- chatState.allowSendAttachmentStateChanged(
- siteInfo == null || siteInfo.allowedFileSenders.isVisitorAllowed
- )
+ chatState.allowSendAttachmentStateChanged(siteInfo == null || siteInfo.allowedFileSenders.isVisitorAllowed)
}
}
private fun observeQueueTicketState() {
Logger.d(TAG, "observeQueueTicketState")
disposable.add(
- ticketStateChangeToUnstaffedUseCase
- .execute()
+ ticketStateChangeToUnstaffedUseCase.execute()
.subscribe({ dialogController.showNoMoreOperatorsAvailableDialog() }) {
Logger.e(TAG, "Error happened while observing queue state : $it")
}
@@ -1762,7 +989,18 @@ internal class ChatController(
return isCallVisualizerUseCase()
}
- companion object {
- private const val EMPTY_MESSAGE = ""
+ fun onGvaButtonClicked(button: GvaButton) {
+ when (val buttonType: Gva.ButtonType = determineGvaButtonTypeUseCase(button)) {
+ Gva.ButtonType.BroadcastEvent -> viewCallback?.showBroadcastNotSupportedToast()
+ is Gva.ButtonType.Email -> viewCallback?.requestOpenEmailClient(buttonType.uri)
+ is Gva.ButtonType.Phone -> viewCallback?.requestOpenDialer(buttonType.uri)
+ is Gva.ButtonType.PostBack -> sendGvaResponse(buttonType.singleChoiceAttachment)
+ is Gva.ButtonType.Url -> viewCallback?.requestOpenUri(buttonType.uri)
+ }
+ }
+
+ private fun scrollChatToBottom() {
+ emitViewState { chatState.copy(isChatInBottom = true) }
+ viewCallback?.smoothScrollToBottom()
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AddNewMessagesDividerUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AddNewMessagesDividerUseCase.kt
index edca2c74c..fa260172a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AddNewMessagesDividerUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AddNewMessagesDividerUseCase.kt
@@ -1,7 +1,7 @@
package com.glia.widgets.chat.domain
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.NewMessagesItem
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.NewMessagesDividerItem
internal class AddNewMessagesDividerUseCase(
private val findNewMessagesDividerIndexUseCase: FindNewMessagesDividerIndexUseCase
@@ -10,7 +10,7 @@ internal class AddNewMessagesDividerUseCase(
val index = findNewMessagesDividerIndexUseCase(messages, unreadMessagesCount)
if (index != NOT_PROVIDED) {
- messages.add(index, NewMessagesItem)
+ messages.add(index, NewMessagesDividerItem)
return true
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendHistoryChatItemUseCases.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendHistoryChatItemUseCases.kt
new file mode 100644
index 000000000..c4d5969b2
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendHistoryChatItemUseCases.kt
@@ -0,0 +1,177 @@
+package com.glia.widgets.chat.domain
+
+import androidx.annotation.VisibleForTesting
+import com.glia.androidsdk.chat.FilesAttachment
+import com.glia.androidsdk.chat.OperatorMessage
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.SystemMessage
+import com.glia.androidsdk.chat.VisitorMessage
+import com.glia.widgets.chat.domain.gva.IsGvaUseCase
+import com.glia.widgets.chat.domain.gva.MapGvaUseCase
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.CustomCardChatItem
+import com.glia.widgets.chat.model.OperatorStatusItem
+import com.glia.widgets.chat.model.SystemChatItem
+import com.glia.widgets.chat.model.VisitorMessageItem
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.asSingleChoice
+
+
+internal class AppendHistoryChatMessageUseCase(
+ private val appendHistoryVisitorChatItemUseCase: AppendHistoryVisitorChatItemUseCase,
+ private val appendHistoryOperatorChatItemUseCase: AppendHistoryOperatorChatItemUseCase,
+ private val appendSystemMessageItemUseCase: AppendSystemMessageItemUseCase
+) {
+ @VisibleForTesting
+ var operatorId: String? = null
+
+ @VisibleForTesting
+ fun resetOperatorId() {
+ operatorId = null
+ }
+
+ @VisibleForTesting
+ fun shouldShowChatHead(chatMessageInternal: ChatMessageInternal): Boolean {
+ if (operatorId != chatMessageInternal.operatorId) {
+ operatorId = chatMessageInternal.operatorId
+ return true
+ }
+
+ return false
+ }
+
+ operator fun invoke(chatItems: MutableList, chatMessageInternal: ChatMessageInternal, isLatest: Boolean) {
+ when (val message = chatMessageInternal.chatMessage) {
+ is VisitorMessage -> {
+ resetOperatorId()
+ appendHistoryVisitorChatItemUseCase(chatItems, message)
+ }
+
+ is OperatorMessage -> appendHistoryOperatorChatItemUseCase(
+ chatItems,
+ chatMessageInternal,
+ isLatest,
+ shouldShowChatHead(chatMessageInternal)
+ )
+
+ is SystemMessage -> {
+ resetOperatorId()
+ appendSystemMessageItemUseCase(chatItems, message)
+ }
+
+ else -> Logger.d(TAG, "Unexpected type of message received -> $message")
+ }
+ }
+}
+
+internal class AppendHistoryOperatorChatItemUseCase(
+ private val isGvaUseCase: IsGvaUseCase,
+ private val customCardAdapterTypeUseCase: CustomCardAdapterTypeUseCase,
+ private val appendGvaMessageItemUseCase: AppendGvaMessageItemUseCase,
+ private val appendHistoryCustomCardItemUseCase: AppendHistoryCustomCardItemUseCase,
+ private val appendHistoryResponseCardOrTextItemUseCase: AppendHistoryResponseCardOrTextItemUseCase
+) {
+ operator fun invoke(chatItems: MutableList, chatMessageInternal: ChatMessageInternal, isLatest: Boolean, showChatHead: Boolean) {
+ val message: OperatorMessage = chatMessageInternal.chatMessage as OperatorMessage
+ when {
+ isGvaUseCase(message) -> appendGvaMessageItemUseCase(chatItems, chatMessageInternal, showChatHead)
+ customCardAdapterTypeUseCase(message) != null -> appendHistoryCustomCardItemUseCase(
+ chatItems,
+ message,
+ customCardAdapterTypeUseCase(message)!!
+ )
+
+ else -> appendHistoryResponseCardOrTextItemUseCase(chatItems, chatMessageInternal, isLatest, showChatHead)
+ }
+ }
+}
+
+internal class AppendHistoryVisitorChatItemUseCase(
+ private val mapVisitorAttachmentUseCase: MapVisitorAttachmentUseCase
+) {
+ operator fun invoke(chatItems: MutableList, message: VisitorMessage) {
+ message.apply {
+ (attachment as? FilesAttachment)?.files?.reversed()?.forEach {
+ chatItems += mapVisitorAttachmentUseCase(it, message)
+ }
+
+ if (content.isNotBlank()) {
+ chatItems += VisitorMessageItem(content, id, timestamp)
+ }
+ }
+
+ }
+}
+
+internal class AppendSystemMessageItemUseCase {
+ operator fun invoke(chatItems: MutableList, message: SystemMessage) {
+ val index = if (chatItems.lastOrNull() is OperatorStatusItem.InQueue) chatItems.lastIndex else chatItems.lastIndex + 1
+ chatItems.add(index, message.run { SystemChatItem(content, id, timestamp) })
+ }
+}
+
+internal class AppendGvaMessageItemUseCase(private val mapGvaUseCase: MapGvaUseCase) {
+ operator fun invoke(chatItems: MutableList, message: ChatMessageInternal, showChatHead: Boolean = true) {
+ chatItems += mapGvaUseCase(message, showChatHead)
+ }
+}
+
+internal class AppendHistoryCustomCardItemUseCase(
+ private val customCardTypeUseCase: CustomCardTypeUseCase,
+ private val customCardShouldShowUseCase: CustomCardShouldShowUseCase,
+) {
+ operator fun invoke(chatItems: MutableList, message: OperatorMessage, viewType: Int) {
+ val customCardType = customCardTypeUseCase.execute(viewType) ?: return
+ if (customCardShouldShowUseCase.execute(message, customCardType, true)) {
+ chatItems.add(message.run { CustomCardChatItem(message, viewType) })
+ }
+
+ message.attachment?.asSingleChoice()?.selectedOptionText?.takeIf {
+ it.isNotBlank()
+ }?.let {
+ VisitorMessageItem(it, message.id, message.timestamp)
+ }?.also {
+ chatItems.add(it)
+ }
+ }
+}
+
+internal class AppendHistoryResponseCardOrTextItemUseCase(
+ private val mapOperatorAttachmentUseCase: MapOperatorAttachmentUseCase,
+ private val mapOperatorPlainTextUseCase: MapOperatorPlainTextUseCase,
+ private val mapResponseCardUseCase: MapResponseCardUseCase
+) {
+ operator fun invoke(chatItems: MutableList, message: ChatMessageInternal, isLatest: Boolean, showChatHead: Boolean) {
+ val chatMessage = message.chatMessage
+ chatMessage.attachment?.asSingleChoice()?.takeIf {
+ it.options.isNotEmpty() && isLatest
+ }?.let { addResponseCard(chatItems, it, message, showChatHead) } ?: addPlainTextAndAttachments(chatItems, message, showChatHead)
+ }
+
+ @VisibleForTesting
+ fun addPlainTextAndAttachments(chatItems: MutableList, message: ChatMessageInternal, showChatHead: Boolean) {
+ val filesAttachment = message.chatMessage.attachment as? FilesAttachment
+
+ filesAttachment?.files?.apply {
+ for (index in indices.reversed()) {
+ chatItems += mapOperatorAttachmentUseCase(get(index), message, showChatHead && index == lastIndex)
+ }
+ }
+
+ if (message.chatMessage.content.isNotBlank()) {
+ chatItems += mapOperatorPlainTextUseCase(message, showChatHead && filesAttachment == null)
+ }
+ }
+
+ @VisibleForTesting
+ fun addResponseCard(
+ chatItems: MutableList,
+ attachment: SingleChoiceAttachment,
+ message: ChatMessageInternal,
+ showChatHead: Boolean
+ ) {
+ chatItems += mapResponseCardUseCase(attachment, message, showChatHead)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendNewChatItemUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendNewChatItemUseCase.kt
new file mode 100644
index 000000000..809122cfa
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/AppendNewChatItemUseCase.kt
@@ -0,0 +1,187 @@
+package com.glia.widgets.chat.domain
+
+import androidx.annotation.VisibleForTesting
+import com.glia.androidsdk.chat.FilesAttachment
+import com.glia.androidsdk.chat.OperatorMessage
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.SystemMessage
+import com.glia.androidsdk.chat.VisitorMessage
+import com.glia.widgets.chat.ChatManager
+import com.glia.widgets.chat.domain.gva.IsGvaUseCase
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.OperatorChatItem
+import com.glia.widgets.chat.model.VisitorChatItem
+import com.glia.widgets.chat.model.VisitorMessageItem
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.asSingleChoice
+
+
+internal class AppendNewChatMessageUseCase(
+ private val appendNewOperatorMessageUseCase: AppendNewOperatorMessageUseCase,
+ private val appendNewVisitorMessageUseCase: AppendNewVisitorMessageUseCase,
+ private val appendSystemMessageItemUseCase: AppendSystemMessageItemUseCase
+) {
+ operator fun invoke(state: ChatManager.State, chatMessageInternal: ChatMessageInternal) {
+ when (val message = chatMessageInternal.chatMessage) {
+ is VisitorMessage -> {
+ appendNewVisitorMessageUseCase(state, chatMessageInternal)
+ state.resetOperator()
+ }
+
+ is OperatorMessage -> appendNewOperatorMessageUseCase(state, chatMessageInternal)
+
+ is SystemMessage -> {
+ appendSystemMessageItemUseCase(state.chatItems, message)
+ state.resetOperator()
+ }
+
+ else -> Logger.d(TAG, "Unexpected type of message received -> $message")
+ }
+ }
+}
+
+internal class AppendNewOperatorMessageUseCase(
+ private val isGvaUseCase: IsGvaUseCase,
+ private val customCardAdapterTypeUseCase: CustomCardAdapterTypeUseCase,
+ private val appendGvaMessageItemUseCase: AppendGvaMessageItemUseCase,
+ private val appendHistoryCustomCardItemUseCase: AppendHistoryCustomCardItemUseCase,
+ private val appendNewResponseCardOrTextItemUseCase: AppendNewResponseCardOrTextItemUseCase
+) {
+ operator fun invoke(state: ChatManager.State, chatMessageInternal: ChatMessageInternal) {
+ val itemsCount = state.chatItems.count()
+ val message: OperatorMessage = chatMessageInternal.chatMessage as OperatorMessage
+ when {
+ isGvaUseCase(message) -> appendGvaMessageItemUseCase(
+ state.chatItems,
+ chatMessageInternal
+ )
+
+ customCardAdapterTypeUseCase(message) != null -> appendHistoryCustomCardItemUseCase(
+ state.chatItems,
+ message,
+ customCardAdapterTypeUseCase(message)!!
+ )
+
+ else -> appendNewResponseCardOrTextItemUseCase(state.chatItems, chatMessageInternal)
+ }
+
+ state.apply { addedMessagesCount = chatItems.count() - itemsCount }
+
+
+ val lastMessageWithVisibleOperatorImage = state.lastMessageWithVisibleOperatorImage
+
+
+ val lastItem = state.chatItems.lastOrNull()
+
+ if (lastItem !is OperatorChatItem) {
+ state.resetOperator()
+ return
+ }
+
+ if (state.isOperatorChanged(lastItem) || lastMessageWithVisibleOperatorImage == null) {
+ return
+ }
+
+ val index = state.chatItems.indexOf(lastMessageWithVisibleOperatorImage)
+ if (index == -1) return
+ state.chatItems[index] = lastMessageWithVisibleOperatorImage.withShowChatHead(false)
+ }
+}
+
+internal class AppendNewResponseCardOrTextItemUseCase(
+ private val mapOperatorAttachmentUseCase: MapOperatorAttachmentUseCase,
+ private val mapOperatorPlainTextUseCase: MapOperatorPlainTextUseCase,
+ private val mapResponseCardUseCase: MapResponseCardUseCase
+) {
+ operator fun invoke(chatItems: MutableList, message: ChatMessageInternal) {
+ val chatMessage = message.chatMessage
+ chatMessage.attachment?.asSingleChoice()?.takeIf {
+ it.options.isNotEmpty()
+ }?.let { addResponseCard(chatItems, it, message) } ?: addPlainTextAndAttachments(chatItems, message)
+ }
+
+ @VisibleForTesting
+ fun addPlainTextAndAttachments(chatItems: MutableList, message: ChatMessageInternal) {
+ val filesAttachment = message.chatMessage.attachment as? FilesAttachment
+
+ if (message.chatMessage.content.isNotBlank()) {
+ chatItems += mapOperatorPlainTextUseCase(message, filesAttachment?.files.isNullOrEmpty())
+ }
+
+ filesAttachment?.files?.apply {
+ for (index in indices) {
+ chatItems += mapOperatorAttachmentUseCase(get(index), message, index == lastIndex)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ fun addResponseCard(
+ chatItems: MutableList,
+ attachment: SingleChoiceAttachment,
+ message: ChatMessageInternal
+ ) {
+ chatItems += mapResponseCardUseCase(attachment, message, true)
+ }
+}
+
+internal class AppendNewVisitorMessageUseCase(
+ private val mapVisitorAttachmentUseCase: MapVisitorAttachmentUseCase
+) {
+
+ @VisibleForTesting
+ var lastDeliveredItem: VisitorChatItem? = null
+
+ @VisibleForTesting
+ fun addUnsentItem(state: ChatManager.State, message: VisitorMessage): Boolean {
+ if (state.unsentItems.isEmpty()) return false
+
+ val unsentMessage = state.unsentItems.firstOrNull { it.content == message.content } ?: return false
+ state.unsentItems.remove(unsentMessage)
+
+ val index = state.chatItems.indexOf(unsentMessage.chatMessage)
+ if (index != -1) {
+ if (lastDeliveredItem != null) {
+ val lastDeliveredIndex = state.chatItems.indexOf(lastDeliveredItem!!)
+ state.chatItems[lastDeliveredIndex] = lastDeliveredItem!!.withDeliveredStatus(false)
+ }
+
+ state.chatItems[index] = message.run {
+ lastDeliveredItem = VisitorMessageItem(content, id, timestamp, true)
+ lastDeliveredItem!!
+ }
+
+ return true
+ }
+
+ return false
+ }
+
+ operator fun invoke(state: ChatManager.State, chatMessageInternal: ChatMessageInternal) {
+ val message = chatMessageInternal.chatMessage as VisitorMessage
+
+ if (!addUnsentItem(state, message)) {
+ message.apply {
+ val files = (attachment as? FilesAttachment)?.files
+ val hasFiles = !files.isNullOrEmpty()
+
+ if (content.isNotBlank()) {
+ state.chatItems += VisitorMessageItem(content, id, timestamp, !hasFiles)
+ }
+
+ files?.forEachIndexed { index, attachmentFile ->
+ state.chatItems += mapVisitorAttachmentUseCase(attachmentFile, message, index == files.lastIndex)
+ }
+
+ if (lastDeliveredItem != null) {
+ val index = state.chatItems.indexOf(lastDeliveredItem!!)
+ state.chatItems[index] = lastDeliveredItem!!.withDeliveredStatus(false)
+ }
+
+ lastDeliveredItem = state.chatItems.last() as? VisitorChatItem
+ }
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.java
deleted file mode 100644
index 18d3afe2a..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.glia.widgets.chat.domain;
-
-import androidx.annotation.Nullable;
-
-import com.glia.androidsdk.chat.ChatMessage;
-import com.glia.widgets.chat.adapter.CustomCardAdapter;
-
-public class CustomCardAdapterTypeUseCase {
- @Nullable
- private final CustomCardAdapter adapter;
-
- public CustomCardAdapterTypeUseCase(@Nullable CustomCardAdapter adapter) {
- this.adapter = adapter;
- }
-
- @Nullable
- public Integer execute(ChatMessage message) {
- if (adapter == null || message.getMetadata() == null || message.getMetadata().length() == 0) {
- return null;
- }
- return adapter.getChatAdapterViewType(message);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.kt
new file mode 100644
index 000000000..196bd6c42
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/CustomCardAdapterTypeUseCase.kt
@@ -0,0 +1,11 @@
+package com.glia.widgets.chat.domain
+
+import com.glia.androidsdk.chat.ChatMessage
+import com.glia.widgets.chat.adapter.CustomCardAdapter
+
+class CustomCardAdapterTypeUseCase(private val adapter: CustomCardAdapter?) {
+ operator fun invoke(message: ChatMessage): Int? = when {
+ adapter == null || message.metadata == null || message.metadata!!.length() == 0 -> null
+ else -> adapter.getChatAdapterViewType(message)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/FindNewMessagesDividerIndexUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/FindNewMessagesDividerIndexUseCase.kt
index d7be5c087..78bc8c225 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/FindNewMessagesDividerIndexUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/FindNewMessagesDividerIndexUseCase.kt
@@ -1,9 +1,10 @@
package com.glia.widgets.chat.domain
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.ServerChatItem
+import com.glia.widgets.chat.model.ChatItem
+import com.glia.widgets.chat.model.ServerChatItem
internal const val NOT_PROVIDED = -1
+
internal class FindNewMessagesDividerIndexUseCase {
/**
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
index bb576679c..20c9c755f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
@@ -3,6 +3,7 @@ package com.glia.widgets.chat.domain
import com.glia.widgets.chat.data.GliaChatRepository
import com.glia.widgets.core.engagement.domain.MapOperatorUseCase
import com.glia.widgets.core.engagement.domain.model.ChatHistoryResponse
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
import com.glia.widgets.core.secureconversations.domain.GetUnreadMessagesCountWithTimeoutUseCase
import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
@@ -30,9 +31,9 @@ internal class GliaLoadHistoryUseCase(
getUnreadMessagesCountUseCase()
) { messages, count -> ChatHistoryResponse(messages, count) }
- private fun loadHistoryAndMapOperator() = loadHistory()
+ private fun loadHistoryAndMapOperator(): Single> = loadHistory()
.flatMapPublisher { Flowable.fromArray(*it) }
- .concatMapSingle { mapOperatorUseCase(it) }
+ .concatMapSingle { mapOperatorUseCase(chatMessage = it) }
.toSortedList(Comparator.comparingLong { it.chatMessage.timestamp })
private fun loadHistory() = Single.create { emitter ->
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.java
deleted file mode 100644
index 2419c8eee..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.glia.widgets.chat.domain;
-
-import com.glia.androidsdk.chat.ChatMessage;
-import com.glia.androidsdk.omnicore.OmnicoreEngagement;
-import com.glia.widgets.chat.data.GliaChatRepository;
-import com.glia.widgets.core.engagement.domain.GliaOnEngagementUseCase;
-import com.glia.widgets.core.engagement.domain.MapOperatorUseCase;
-import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal;
-
-import io.reactivex.Observable;
-import io.reactivex.subjects.PublishSubject;
-
-public class GliaOnMessageUseCase implements
- GliaOnEngagementUseCase.Listener,
- GliaChatRepository.MessageListener {
-
- private final GliaOnEngagementUseCase onEngagementUseCase;
- private final GliaChatRepository messageRepository;
- private final MapOperatorUseCase mapOperatorUseCase;
- private final PublishSubject publishSubject;
-
- public GliaOnMessageUseCase(
- GliaChatRepository messageRepository,
- GliaOnEngagementUseCase gliaOnEngagementUseCase,
- MapOperatorUseCase mapOperatorUseCase) {
- this.onEngagementUseCase = gliaOnEngagementUseCase;
- this.messageRepository = messageRepository;
- this.mapOperatorUseCase = mapOperatorUseCase;
- publishSubject = PublishSubject.create();
- }
-
- public Observable execute() {
- this.onEngagementUseCase.execute(this);
- return publishSubject
- .flatMapSingle(mapOperatorUseCase::invoke)
- .doOnError(Throwable::printStackTrace)
- .share();
- }
-
- public void unregisterListener() {
- messageRepository.unregisterEngagementMessageListener(this);
- onEngagementUseCase.unregisterListener(this);
- }
-
- @Override
- public void newEngagementLoaded(OmnicoreEngagement engagement) {
- messageRepository.listenForEngagementMessages(this, engagement);
- }
-
- @Override
- public void onMessage(ChatMessage chatMessage) {
- publishSubject.onNext(chatMessage);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.kt
new file mode 100644
index 000000000..e85ee7c28
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaOnMessageUseCase.kt
@@ -0,0 +1,27 @@
+package com.glia.widgets.chat.domain
+
+import com.glia.androidsdk.chat.ChatMessage
+import com.glia.widgets.chat.data.GliaChatRepository
+import com.glia.widgets.core.engagement.domain.MapOperatorUseCase
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import io.reactivex.Observable
+import java.util.function.Consumer
+
+internal class GliaOnMessageUseCase(
+ private val messageRepository: GliaChatRepository,
+ private val mapOperatorUseCase: MapOperatorUseCase
+) {
+
+ private val observable = Observable.create { observer ->
+ val messageListener = Consumer { observer.onNext(it) }
+
+ messageRepository.listenForAllMessages(messageListener)
+
+ observer.setCancellable { messageRepository.unregisterAllMessageListener(messageListener) }
+ }
+ .flatMapSingle { mapOperatorUseCase(it) }
+ .doOnError { it.printStackTrace() }
+ .share()
+
+ operator fun invoke(): Observable = observable
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
index 45a3e551e..a5c1f8b5f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
@@ -1,12 +1,11 @@
package com.glia.widgets.chat.domain
import com.glia.androidsdk.GliaException
-import com.glia.androidsdk.chat.ChatMessage
import com.glia.androidsdk.chat.FilesAttachment
-import com.glia.androidsdk.chat.OperatorMessage
import com.glia.androidsdk.chat.SingleChoiceAttachment
import com.glia.androidsdk.chat.VisitorMessage
import com.glia.widgets.chat.data.GliaChatRepository
+import com.glia.widgets.chat.model.Unsent
import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.engagement.GliaEngagementStateRepository
import com.glia.widgets.core.fileupload.FileAttachmentRepository
@@ -14,7 +13,7 @@ import com.glia.widgets.core.fileupload.model.FileAttachment
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
-class GliaSendMessageUseCase(
+internal class GliaSendMessageUseCase(
private val chatRepository: GliaChatRepository,
private val fileAttachmentRepository: FileAttachmentRepository,
private val engagementStateRepository: GliaEngagementStateRepository,
@@ -24,11 +23,13 @@ class GliaSendMessageUseCase(
) {
interface Listener {
fun messageSent(message: VisitorMessage?)
- fun onCardMessageUpdated(message: ChatMessage)
fun onMessageValidated()
- fun errorOperatorNotOnline(message: String)
- fun errorMessageInvalid()
+ fun errorOperatorNotOnline(message: Unsent)
fun error(ex: GliaException)
+
+ fun errorMessageInvalid() {
+ // Currently, no need for this method, but have to keep it because it describes case in else branch
+ }
}
private val isSecureEngagement: Boolean
@@ -79,44 +80,22 @@ class GliaSendMessageUseCase(
sendMessage(message, listener)
}
} else {
- listener.errorOperatorNotOnline(message)
+ listener.errorOperatorNotOnline(Unsent.Message(message))
}
} else {
listener.errorMessageInvalid()
}
}
- fun execute(singleChoiceAttachment: SingleChoiceAttachment?, listener: Listener?) {
- chatRepository.sendMessageSingleChoice(singleChoiceAttachment, listener)
- }
+ fun execute(singleChoiceAttachment: SingleChoiceAttachment, listener: Listener) {
+ when {
+ isSecureEngagement -> singleChoiceAttachment.apply {
+ secureConversationsRepository.send(selectedOptionText, engagementConfigRepository.queueIds, singleChoiceAttachment, listener)
+ }
- fun execute(chatMessage: ChatMessage?, text: String, value: String, listener: Listener?) {
- val attachment = SingleChoiceAttachment.from(value, text)
- chatRepository.sendResponse(attachment) { result: VisitorMessage?, ex: GliaException? ->
- listener?.let {
- if (ex != null) {
- listener.error(ex)
- }
- if (result != null) {
- listener.messageSent(result)
+ isOperatorOnline -> chatRepository.sendMessageSingleChoice(singleChoiceAttachment, listener)
- chatMessage?.let {
- it as? OperatorMessage
- }?.let {
- listener.onCardMessageUpdated(
- ChatMessage(
- it.id,
- it.content,
- it.timestamp,
- ChatMessage.Sender(it.senderType, it.operatorHref, it.operatorId),
- it.deliveredAt,
- attachment,
- it.metadata
- )
- )
- }
- }
- }
+ else -> listener.errorOperatorNotOnline(Unsent.Attachment(singleChoiceAttachment))
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/HandleCustomCardClickUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/HandleCustomCardClickUseCase.kt
new file mode 100644
index 000000000..6bd9bd7bc
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/HandleCustomCardClickUseCase.kt
@@ -0,0 +1,48 @@
+package com.glia.widgets.chat.domain
+
+import com.glia.androidsdk.chat.ChatMessage
+import com.glia.androidsdk.chat.OperatorMessage
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.widgets.chat.ChatManager
+import com.glia.widgets.chat.model.CustomCardChatItem
+
+internal class HandleCustomCardClickUseCase(
+ private val customCardTypeUseCase: CustomCardTypeUseCase,
+ private val customCardShouldShowUseCase: CustomCardShouldShowUseCase
+) {
+ operator fun invoke(
+ customCard: CustomCardChatItem,
+ attachment: SingleChoiceAttachment,
+ state: ChatManager.State
+ ): ChatManager.State {
+ val customCardType = customCardTypeUseCase.execute(customCard.viewType) ?: return state
+ val currentMessage = customCard.message
+ val showCustomCard = customCardShouldShowUseCase.execute(
+ currentMessage,
+ customCardType,
+ false
+ )
+ if (!showCustomCard) {
+ state.chatItems.remove(customCard)
+ } else {
+ val index = state.chatItems.indexOf(customCard)
+ val message = (currentMessage as? OperatorMessage)
+ if (index != -1 && message != null) {
+ val newMessage = message.let {
+ ChatMessage(
+ it.id,
+ it.content,
+ it.timestamp,
+ ChatMessage.Sender(it.senderType, it.operatorHref, it.operatorId),
+ it.deliveredAt,
+ attachment,
+ it.metadata
+ )
+ }
+
+ state.chatItems[index] = CustomCardChatItem(newMessage, customCard.viewType)
+ }
+ }
+ return state
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsEnableChatEditTextUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsEnableChatEditTextUseCase.kt
deleted file mode 100644
index 4373a4737..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsEnableChatEditTextUseCase.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.glia.widgets.chat.domain
-
-import com.glia.widgets.chat.adapter.ChatAdapter
-import com.glia.widgets.chat.model.history.ChatItem
-import com.glia.widgets.chat.model.history.ResponseCardItem
-
-class IsEnableChatEditTextUseCase {
-
- // should enable only if there is no unselected choice-card last
- operator fun invoke(items: List?): Boolean = items?.lastOrNull()?.let {
- it.viewType != ChatAdapter.OPERATOR_MESSAGE_VIEW_TYPE || it !is ResponseCardItem
- } ?: true
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
index 4004c7283..159c813d8 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
@@ -17,7 +17,7 @@ class IsShowSendButtonUseCase(
}
private fun hasText(message: String?): Boolean {
- return message != null && message.isNotEmpty()
+ return !message.isNullOrEmpty()
}
private fun hadReadyToSendUnsentAttachments(): Boolean {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/MapChatItemUseCases.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/MapChatItemUseCases.kt
new file mode 100644
index 000000000..6d2082835
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/MapChatItemUseCases.kt
@@ -0,0 +1,77 @@
+package com.glia.widgets.chat.domain
+
+import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.VisitorMessage
+import com.glia.widgets.chat.model.OperatorAttachmentItem
+import com.glia.widgets.chat.model.OperatorMessageItem
+import com.glia.widgets.chat.model.VisitorAttachmentItem
+import com.glia.widgets.chat.model.VisitorChatItem
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.helper.isImage
+import kotlin.jvm.optionals.getOrNull
+
+internal class MapOperatorAttachmentUseCase {
+ operator fun invoke(attachment: AttachmentFile, chatMessageInternal: ChatMessageInternal, showChatHead: Boolean) = chatMessageInternal.run {
+ if (attachment.isImage) {
+ OperatorAttachmentItem.Image(
+ attachmentFile = attachment,
+ id = chatMessage.id,
+ timestamp = chatMessage.timestamp,
+ showChatHead = showChatHead,
+ operatorProfileImgUrl = operatorImageUrl,
+ operatorId = operatorId
+ )
+ } else {
+ OperatorAttachmentItem.File(
+ attachmentFile = attachment,
+ id = chatMessage.id,
+ timestamp = chatMessage.timestamp,
+ showChatHead = showChatHead,
+ operatorProfileImgUrl = operatorImageUrl,
+ operatorId = operatorId
+ )
+ }
+ }
+}
+
+internal class MapVisitorAttachmentUseCase {
+ operator fun invoke(attachmentFile: AttachmentFile, message: VisitorMessage, showDelivered: Boolean = false): VisitorChatItem = message.run {
+ if (attachmentFile.isImage) {
+ VisitorAttachmentItem.Image(id, timestamp, attachmentFile, showDelivered = showDelivered)
+ } else {
+ VisitorAttachmentItem.File(id, timestamp, attachmentFile, showDelivered = showDelivered)
+ }
+ }
+}
+
+internal class MapOperatorPlainTextUseCase {
+ operator fun invoke(chatMessageInternal: ChatMessageInternal, showChatHead: Boolean): OperatorMessageItem = chatMessageInternal.run {
+ OperatorMessageItem.PlainText(
+ chatMessage.id,
+ chatMessage.timestamp,
+ showChatHead,
+ operatorImageUrl,
+ operatorId,
+ operatorName,
+ chatMessage.content
+ )
+ }
+}
+
+internal class MapResponseCardUseCase {
+ operator fun invoke(attachment: SingleChoiceAttachment, message: ChatMessageInternal, showChatHead: Boolean): OperatorMessageItem.ResponseCard =
+ message.run {
+ OperatorMessageItem.ResponseCard(
+ chatMessage.id,
+ chatMessage.timestamp,
+ showChatHead,
+ operatorImageUrl,
+ operatorId,
+ operatorName,
+ chatMessage.content,
+ attachment.options.asList(),
+ attachment.imageUrl.getOrNull()
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/PreEngagementMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/PreEngagementMessageUseCase.kt
deleted file mode 100644
index add4d49d6..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/PreEngagementMessageUseCase.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.glia.widgets.chat.domain
-
-import com.glia.androidsdk.chat.ChatMessage
-import com.glia.widgets.chat.data.GliaChatRepository
-import com.glia.widgets.core.engagement.GliaEngagementRepository
-import com.glia.widgets.core.engagement.domain.GliaOnEngagementUseCase
-import com.glia.widgets.core.engagement.domain.MapOperatorUseCase
-import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
-import io.reactivex.Observable
-import java.util.function.Consumer
-
-internal class PreEngagementMessageUseCase(
- private val messageRepository: GliaChatRepository,
- private val engagementRepository: GliaEngagementRepository,
- private val onEngagementUseCase: GliaOnEngagementUseCase,
- private val mapOperatorUseCase: MapOperatorUseCase
-) {
-
- fun execute(): Observable {
- if (engagementRepository.hasOngoingEngagement()) {
- return Observable.empty()
- }
- return Observable.create { observer ->
- val messageListener = Consumer { chatMessage ->
- observer.onNext(chatMessage)
- }
-
- val engagementListener = GliaOnEngagementUseCase.Listener {
- observer.onComplete()
- }
-
- messageRepository.listenForAllMessages(messageListener)
- onEngagementUseCase.execute(engagementListener)
-
- observer.setCancellable {
- messageRepository.unregisterAllMessageListener(messageListener)
- onEngagementUseCase.unregisterListener(engagementListener)
- }
- }
- .flatMapSingle { chatMessage: ChatMessage -> mapOperatorUseCase(chatMessage) }
- .doOnError { obj: Throwable -> obj.printStackTrace() }
- .share()
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt
new file mode 100644
index 000000000..ad159c6f6
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt
@@ -0,0 +1,32 @@
+package com.glia.widgets.chat.domain
+
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.VisitorMessage
+import com.glia.widgets.chat.data.GliaChatRepository
+import com.glia.widgets.chat.model.Unsent
+
+
+internal class SendUnsentMessagesUseCase(private val chatRepository: GliaChatRepository) {
+ operator fun invoke(message: Unsent, onSuccess: (VisitorMessage) -> Unit) {
+ when (message) {
+ is Unsent.Attachment -> sendAttachment(message.attachment, onSuccess)
+ is Unsent.Message -> sendMessage(message.message, onSuccess)
+ }
+ }
+
+ private fun sendAttachment(attachment: SingleChoiceAttachment, onSuccess: (VisitorMessage) -> Unit) {
+ chatRepository.sendResponse(attachment) { response, exception ->
+ if (exception == null && response != null) {
+ onSuccess(response)
+ }
+ }
+ }
+
+ private fun sendMessage(message: String, onSuccess: (VisitorMessage) -> Unit) {
+ chatRepository.sendMessage(message) { response, exception ->
+ if (exception == null && response != null) {
+ onSuccess(response)
+ }
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaButtonTypeUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaButtonTypeUseCase.kt
new file mode 100644
index 000000000..5807161c6
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaButtonTypeUseCase.kt
@@ -0,0 +1,16 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaButton
+
+internal const val PHONE_SCHEME = "tel"
+internal const val EMAIL_SCHEME = "mailto"
+
+internal class DetermineGvaButtonTypeUseCase(private val determineGvaUrlTypeUseCase: DetermineGvaUrlTypeUseCase) {
+
+ operator fun invoke(button: GvaButton): Gva.ButtonType = when {
+ !button.destinationPbBroadcastEvent.isNullOrBlank() -> Gva.ButtonType.BroadcastEvent
+ button.url.isNullOrBlank() -> Gva.ButtonType.PostBack(button.toResponse())
+ else -> determineGvaUrlTypeUseCase(button.url)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaUrlTypeUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaUrlTypeUseCase.kt
new file mode 100644
index 000000000..28da2474f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/DetermineGvaUrlTypeUseCase.kt
@@ -0,0 +1,16 @@
+package com.glia.widgets.chat.domain.gva
+
+import android.net.Uri
+import com.glia.widgets.chat.model.Gva
+
+internal class DetermineGvaUrlTypeUseCase {
+
+ operator fun invoke(url: String): Gva.ButtonType {
+ val uri = Uri.parse(url)
+ return when (uri.scheme) {
+ PHONE_SCHEME -> Gva.ButtonType.Phone(uri)
+ EMAIL_SCHEME -> Gva.ButtonType.Email(uri)
+ else -> Gva.ButtonType.Url(uri)
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/GetGvaTypeUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/GetGvaTypeUseCase.kt
new file mode 100644
index 000000000..d6705f91f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/GetGvaTypeUseCase.kt
@@ -0,0 +1,10 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import org.json.JSONObject
+
+internal class GetGvaTypeUseCase {
+
+ operator fun invoke(metadata: JSONObject): Gva.Type? = Gva.Type.values().firstOrNull { it.value == metadata.optString(Gva.Keys.TYPE) }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/IsGvaUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/IsGvaUseCase.kt
new file mode 100644
index 000000000..7b83cb927
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/IsGvaUseCase.kt
@@ -0,0 +1,12 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.androidsdk.chat.ChatMessage
+
+internal class IsGvaUseCase(
+ private val getGvaTypeUseCase: GetGvaTypeUseCase
+) {
+
+ operator fun invoke(chatMessage: ChatMessage): Boolean = chatMessage.metadata?.let {
+ getGvaTypeUseCase(it)
+ } != null
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaGvaGalleryCardsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaGvaGalleryCardsUseCase.kt
new file mode 100644
index 000000000..d7393d2a1
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaGvaGalleryCardsUseCase.kt
@@ -0,0 +1,23 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.GvaGalleryCards
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+
+internal class MapGvaGvaGalleryCardsUseCase(
+ private val parseGvaGalleryCardsUseCase: ParseGvaGalleryCardsUseCase
+) {
+ operator fun invoke(chatMessage: ChatMessageInternal, showChatHead: Boolean): GvaGalleryCards {
+ val message = chatMessage.chatMessage
+ val metadata = message.metadata
+
+ return GvaGalleryCards(
+ id = message.id,
+ galleryCards = parseGvaGalleryCardsUseCase(metadata),
+ showChatHead = showChatHead,
+ operatorId = chatMessage.operatorId,
+ timestamp = message.timestamp,
+ operatorProfileImgUrl = chatMessage.operatorImageUrl,
+ operatorName = chatMessage.operatorName
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaPersistentButtonsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaPersistentButtonsUseCase.kt
new file mode 100644
index 000000000..7420574e3
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaPersistentButtonsUseCase.kt
@@ -0,0 +1,25 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaPersistentButtons
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+
+internal class MapGvaPersistentButtonsUseCase(
+ private val parseGvaButtonsUseCase: ParseGvaButtonsUseCase
+) {
+ operator fun invoke(chatMessage: ChatMessageInternal, showChatHead: Boolean): GvaPersistentButtons {
+ val message = chatMessage.chatMessage
+ val metadata = message.metadata
+
+ return GvaPersistentButtons(
+ id = message.id,
+ content = metadata?.optString(Gva.Keys.CONTENT).orEmpty(),
+ options = parseGvaButtonsUseCase(metadata),
+ showChatHead = showChatHead,
+ operatorId = chatMessage.operatorId,
+ timestamp = message.timestamp,
+ operatorProfileImgUrl = chatMessage.operatorImageUrl,
+ operatorName = chatMessage.operatorName
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaQuickRepliesUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaQuickRepliesUseCase.kt
new file mode 100644
index 000000000..3fb4deb5e
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaQuickRepliesUseCase.kt
@@ -0,0 +1,21 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaQuickReplies
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+
+internal class MapGvaQuickRepliesUseCase(private val parseGvaButtonsUseCase: ParseGvaButtonsUseCase) {
+ operator fun invoke(message: ChatMessageInternal, showChatHead: Boolean): GvaQuickReplies =
+ message.run {
+ GvaQuickReplies(
+ id = chatMessage.id,
+ content = chatMessage.metadata?.optString(Gva.Keys.CONTENT).orEmpty(),
+ showChatHead = showChatHead,
+ operatorId = operatorId,
+ timestamp = chatMessage.timestamp,
+ operatorProfileImgUrl = operatorImageUrl,
+ operatorName = operatorName,
+ options = parseGvaButtonsUseCase(chatMessage.metadata)
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaResponseTextUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaResponseTextUseCase.kt
new file mode 100644
index 000000000..026e56662
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaResponseTextUseCase.kt
@@ -0,0 +1,21 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaResponseText
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+
+internal class MapGvaResponseTextUseCase {
+ operator fun invoke(chatMessage: ChatMessageInternal, showChatHead: Boolean): GvaResponseText {
+ val message = chatMessage.chatMessage
+
+ return GvaResponseText(
+ id = message.id,
+ content = message.metadata?.optString(Gva.Keys.CONTENT).orEmpty(),
+ showChatHead = showChatHead,
+ operatorId = chatMessage.operatorId,
+ timestamp = message.timestamp,
+ operatorProfileImgUrl = chatMessage.operatorImageUrl,
+ operatorName = chatMessage.operatorName
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaUseCase.kt
new file mode 100644
index 000000000..c3686039c
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/MapGvaUseCase.kt
@@ -0,0 +1,22 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.OperatorChatItem
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+
+internal class MapGvaUseCase(
+ private val getGvaTypeUseCase: GetGvaTypeUseCase,
+ private val mapGvaResponseTextUseCase: MapGvaResponseTextUseCase,
+ private val mapGvaPersistentButtonsUseCase: MapGvaPersistentButtonsUseCase,
+ private val mapGvaQuickRepliesUseCase: MapGvaQuickRepliesUseCase,
+ private val mapGvaGvaGalleryCardsUseCase: MapGvaGvaGalleryCardsUseCase
+) {
+ operator fun invoke(chatMessageInternal: ChatMessageInternal, showChatHead: Boolean = true): OperatorChatItem =
+ when (getGvaTypeUseCase(chatMessageInternal.chatMessage.metadata!!)) {
+ Gva.Type.PLAIN_TEXT -> mapGvaResponseTextUseCase(chatMessageInternal, showChatHead)
+ Gva.Type.PERSISTENT_BUTTONS -> mapGvaPersistentButtonsUseCase(chatMessageInternal, showChatHead)
+ Gva.Type.QUICK_REPLIES -> mapGvaQuickRepliesUseCase(chatMessageInternal, showChatHead)
+ Gva.Type.GALLERY_CARDS -> mapGvaGvaGalleryCardsUseCase(chatMessageInternal, showChatHead)
+ else -> throw IllegalArgumentException("metadata should contain on of the [${Gva.Type.values().joinToString { it.value }}] types")
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaButtonsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaButtonsUseCase.kt
new file mode 100644
index 000000000..3fa7327af
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaButtonsUseCase.kt
@@ -0,0 +1,13 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaButton
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import org.json.JSONObject
+
+internal class ParseGvaButtonsUseCase(private val gson: Gson) {
+ operator fun invoke(metadata: JSONObject?): List = metadata?.optString(Gva.Keys.OPTIONS)?.let {
+ gson.fromJson(it, object : TypeToken>() {}.type)
+ } ?: emptyList()
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaGalleryCardsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaGalleryCardsUseCase.kt
new file mode 100644
index 000000000..82a680874
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/gva/ParseGvaGalleryCardsUseCase.kt
@@ -0,0 +1,13 @@
+package com.glia.widgets.chat.domain.gva
+
+import com.glia.widgets.chat.model.Gva
+import com.glia.widgets.chat.model.GvaGalleryCard
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import org.json.JSONObject
+
+internal class ParseGvaGalleryCardsUseCase(private val gson: Gson) {
+ operator fun invoke(metadata: JSONObject?): List = metadata?.optString(Gva.Keys.GALLERY_CARDS)?.let {
+ gson.fromJson(it, object : TypeToken>() {}.type)
+ } ?: emptyList()
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt
new file mode 100644
index 000000000..b87b21a87
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt
@@ -0,0 +1,253 @@
+package com.glia.widgets.chat.model
+
+import android.content.Context
+import android.text.format.DateUtils
+import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.androidsdk.chat.ChatMessage
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.glia.androidsdk.chat.SingleChoiceOption
+import com.glia.widgets.chat.adapter.ChatAdapter
+import com.glia.widgets.helper.isDownloaded
+import java.util.UUID
+
+internal abstract class ChatItem(@ChatAdapter.Type val viewType: Int) {
+ abstract val id: String
+ abstract val timestamp: Long
+
+ open fun areContentsTheSame(newItem: ChatItem): Boolean = this == newItem
+}
+
+/**
+ * This is the same as [ChatItem], but should be used for non-local chat items
+ */
+internal abstract class ServerChatItem(@ChatAdapter.Type viewType: Int) : ChatItem(viewType)
+
+internal abstract class OperatorChatItem(@ChatAdapter.Type viewType: Int) : ServerChatItem(viewType) {
+ abstract val showChatHead: Boolean
+ abstract val operatorProfileImgUrl: String?
+ abstract val operatorId: String?
+
+ abstract fun withShowChatHead(showChatHead: Boolean): OperatorChatItem
+}
+
+internal interface AttachmentItem {
+ val attachmentFile: AttachmentFile
+ val isFileExists: Boolean
+ val isDownloading: Boolean
+
+ val attachmentId: String get() = attachmentFile.id
+
+ fun isDownloaded(context: Context): Boolean = attachmentFile.isDownloaded(context)
+
+ fun updateWith(isFileExists: Boolean, isDownloading: Boolean): ChatItem
+}
+
+internal data class CustomCardChatItem(
+ val message: ChatMessage, private val customCardViewType: Int
+) : ServerChatItem(customCardViewType) {
+ override val id: String = message.id
+ override val timestamp: Long = message.timestamp
+}
+
+internal class SystemChatItem(
+ val message: String,
+ override val id: String,
+ override val timestamp: Long
+) : ServerChatItem(ChatAdapter.SYSTEM_MESSAGE_TYPE)
+
+internal sealed class OperatorAttachmentItem(@ChatAdapter.Type viewType: Int) : OperatorChatItem(viewType), AttachmentItem {
+
+ data class Image(
+ override val isFileExists: Boolean = false,
+ override val isDownloading: Boolean = false,
+ override val attachmentFile: AttachmentFile,
+ override val id: String,
+ override val timestamp: Long,
+ override val showChatHead: Boolean = false,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorId: String? = null
+ ) : OperatorAttachmentItem(ChatAdapter.OPERATOR_IMAGE_VIEW_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+
+ override fun updateWith(isFileExists: Boolean, isDownloading: Boolean): ChatItem =
+ copy(isFileExists = isFileExists, isDownloading = isDownloading)
+ }
+
+ data class File(
+ override val isFileExists: Boolean = false,
+ override val isDownloading: Boolean = false,
+ override val attachmentFile: AttachmentFile,
+ override val id: String,
+ override val timestamp: Long,
+ override val showChatHead: Boolean = false,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorId: String? = null
+ ) : OperatorAttachmentItem(ChatAdapter.OPERATOR_FILE_VIEW_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+ override fun updateWith(isFileExists: Boolean, isDownloading: Boolean): ChatItem =
+ copy(isFileExists = isFileExists, isDownloading = isDownloading)
+ }
+}
+
+internal sealed class OperatorMessageItem : OperatorChatItem(ChatAdapter.OPERATOR_MESSAGE_VIEW_TYPE) {
+ abstract val operatorName: String?
+ abstract val content: String?
+
+ data class PlainText(
+ override val id: String,
+ override val timestamp: Long,
+ override val showChatHead: Boolean,
+ override val operatorProfileImgUrl: String?,
+ override val operatorId: String?,
+ override val operatorName: String?,
+ override val content: String?
+ ) : OperatorMessageItem() {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+
+ fun asPlainText(): PlainText = PlainText(
+ id = id,
+ timestamp = timestamp,
+ showChatHead = showChatHead,
+ operatorProfileImgUrl = operatorProfileImgUrl,
+ operatorId = operatorId,
+ operatorName = operatorName,
+ content = content
+ )
+ }
+
+ data class ResponseCard(
+ override val id: String,
+ override val timestamp: Long,
+ override val showChatHead: Boolean,
+ override val operatorProfileImgUrl: String?,
+ override val operatorId: String?,
+ override val operatorName: String?,
+ override val content: String?,
+ val singleChoiceOptions: List,
+ val choiceCardImageUrl: String?
+ ) : OperatorMessageItem() {
+
+ init {
+ require(singleChoiceOptions.isNotEmpty()) { "Response card should have at least one `SingleChoiceOption`" }
+ }
+
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+
+ fun asPlainText() = PlainText(
+ id = id,
+ timestamp = timestamp,
+ showChatHead = showChatHead,
+ operatorProfileImgUrl = operatorProfileImgUrl,
+ operatorId = operatorId,
+ operatorName = operatorName,
+ content = content
+ )
+ }
+}
+
+// Local
+
+internal sealed class MediaUpgradeStartedTimerItem : ChatItem(ChatAdapter.MEDIA_UPGRADE_ITEM_TYPE) {
+ override val id: String = "media_upgrade_item"
+ override val timestamp: Long = -1
+ abstract val time: String
+
+ abstract fun updateTime(time: String): MediaUpgradeStartedTimerItem
+
+ data class Audio(override val time: String = DateUtils.formatElapsedTime(0)) : MediaUpgradeStartedTimerItem() {
+ override fun updateTime(time: String) = copy(time = time)
+ }
+
+ data class Video(override val time: String = DateUtils.formatElapsedTime(0)) : MediaUpgradeStartedTimerItem() {
+ override fun updateTime(time: String) = copy(time = time)
+ }
+}
+
+internal object NewMessagesDividerItem : ChatItem(ChatAdapter.NEW_MESSAGES_DIVIDER_TYPE) {
+ override val id: String = "new_messages_item"
+ override val timestamp: Long = -1
+}
+
+internal sealed class OperatorStatusItem : ChatItem(ChatAdapter.OPERATOR_STATUS_VIEW_TYPE) {
+ override val id: String = "operator_status_item"
+ override val timestamp: Long = -1
+
+ abstract val companyName: String?
+
+ data class InQueue(override val companyName: String?) : OperatorStatusItem()
+
+ data class Connected(
+ override val companyName: String?,
+ val operatorName: String,
+ val profileImgUrl: String?
+ ) : OperatorStatusItem()
+
+ data class Joined(
+ override val companyName: String?,
+ val operatorName: String,
+ val profileImgUrl: String?
+ ) : OperatorStatusItem()
+
+ object Transferring : OperatorStatusItem() {
+ override val companyName: String? = null
+ }
+
+}
+
+// Visitor
+
+internal abstract class VisitorChatItem(@ChatAdapter.Type viewType: Int) : ChatItem(viewType) {
+ abstract val showDelivered: Boolean
+ abstract fun withDeliveredStatus(delivered: Boolean): VisitorChatItem
+}
+
+internal sealed class VisitorAttachmentItem(@ChatAdapter.Type viewType: Int) : VisitorChatItem(viewType), AttachmentItem {
+
+ data class Image(
+ override val id: String,
+ override val timestamp: Long,
+ override val attachmentFile: AttachmentFile,
+ override val isFileExists: Boolean = false,
+ override val isDownloading: Boolean = false,
+ override val showDelivered: Boolean = false
+ ) : VisitorAttachmentItem(ChatAdapter.VISITOR_IMAGE_VIEW_TYPE) {
+ override fun withDeliveredStatus(delivered: Boolean): VisitorChatItem = copy(showDelivered = delivered)
+
+ override fun updateWith(isFileExists: Boolean, isDownloading: Boolean): ChatItem =
+ copy(isFileExists = isFileExists, isDownloading = isDownloading)
+ }
+
+ data class File(
+ override val id: String,
+ override val timestamp: Long,
+ override val attachmentFile: AttachmentFile,
+ override val isFileExists: Boolean = false,
+ override val isDownloading: Boolean = false,
+ override val showDelivered: Boolean = false
+ ) : VisitorAttachmentItem(ChatAdapter.VISITOR_FILE_VIEW_TYPE) {
+ override fun withDeliveredStatus(delivered: Boolean): VisitorChatItem = copy(showDelivered = delivered)
+
+ override fun updateWith(isFileExists: Boolean, isDownloading: Boolean): ChatItem =
+ copy(isFileExists = isFileExists, isDownloading = isDownloading)
+ }
+}
+
+internal data class VisitorMessageItem(
+ val message: String,
+ override val id: String = UUID.randomUUID().toString(),
+ override val timestamp: Long = System.currentTimeMillis(),
+ override val showDelivered: Boolean = false
+) : VisitorChatItem(ChatAdapter.VISITOR_MESSAGE_TYPE) {
+
+ override fun withDeliveredStatus(delivered: Boolean): VisitorChatItem {
+ check(!delivered) { "The method should be called only with false value, to hide delivered status" }
+ return copy(showDelivered = delivered)
+ }
+}
+
+internal sealed class Unsent(val content: String) {
+ val chatMessage: VisitorMessageItem = VisitorMessageItem(message = content)
+
+ data class Message(val message: String) : Unsent(message)
+ data class Attachment(val attachment: SingleChoiceAttachment) : Unsent(attachment.selectedOptionText)
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.java
deleted file mode 100644
index b245be4de..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.java
+++ /dev/null
@@ -1,525 +0,0 @@
-package com.glia.widgets.chat.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.glia.widgets.chat.model.history.ChatItem;
-import com.glia.widgets.chat.model.history.MediaUpgradeStartedTimerItem;
-import com.glia.widgets.chat.model.history.OperatorStatusItem;
-import com.glia.widgets.chat.model.history.VisitorMessageItem;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class ChatState {
- public final boolean integratorChatStarted;
- public final boolean isVisible;
- public final boolean isChatInBottom;
- public final Integer messagesNotSeen;
- private final String formattedOperatorName;
- public final String operatorProfileImgUrl;
- public final String companyName;
- public final String queueId;
- public final String visitorContextAssetId;
- public final MediaUpgradeStartedTimerItem mediaUpgradeStartedTimerItem;
- public final List chatItems;
- public final ChatInputMode chatInputMode;
- public final String lastTypedText;
- public final boolean engagementRequested;
- public final String pendingNavigationType;
- public final List unsentMessages;
- public final OperatorStatusItem operatorStatusItem;
- public final boolean showSendButton;
- public final boolean isAttachmentButtonEnabled;
- public final boolean isAttachmentButtonNeeded;
- public final boolean isOperatorTyping;
- public final boolean isAttachmentAllowed;
- public final boolean isSecureMessaging;
-
- private ChatState(Builder builder) {
- this.formattedOperatorName = builder.formattedOperatorName;
- this.operatorProfileImgUrl = builder.operatorProfileImgUrl;
- this.companyName = builder.companyName;
- this.queueId = builder.queueId;
- this.visitorContextAssetId = builder.visitorContextAssetId;
- this.isVisible = builder.isVisible;
- this.integratorChatStarted = builder.integratorChatStarted;
- this.mediaUpgradeStartedTimerItem = builder.mediaUpgradeStartedTimerItem;
- this.chatItems = Collections.unmodifiableList(builder.chatItems);
- this.chatInputMode = builder.chatInputMode;
- this.lastTypedText = builder.lastTypedText;
- this.isChatInBottom = builder.isChatInBottom;
- this.messagesNotSeen = builder.messagesNotSeen;
- this.engagementRequested = builder.engagementRequested;
- this.pendingNavigationType = builder.pendingNavigationType;
- this.unsentMessages = builder.unsentMessages;
- this.operatorStatusItem = builder.operatorStatusItem;
- this.showSendButton = builder.showSendButton;
- this.isOperatorTyping = builder.isOperatorTyping;
- this.isAttachmentButtonEnabled = builder.isAttachmentButtonEnabled;
- this.isAttachmentButtonNeeded = builder.isAttachmentButtonNeeded;
- this.isAttachmentAllowed = builder.isAttachmentAllowed;
- this.isSecureMessaging = builder.isSecureMessaging;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ChatState chatState = (ChatState) o;
- return integratorChatStarted == chatState.integratorChatStarted &&
- isVisible == chatState.isVisible &&
- Objects.equals(formattedOperatorName, chatState.formattedOperatorName) &&
- Objects.equals(operatorProfileImgUrl, chatState.operatorProfileImgUrl) &&
- Objects.equals(companyName, chatState.companyName) &&
- Objects.equals(queueId, chatState.queueId) &&
- Objects.equals(visitorContextAssetId, chatState.visitorContextAssetId) &&
- Objects.equals(mediaUpgradeStartedTimerItem, chatState.mediaUpgradeStartedTimerItem) &&
- Objects.equals(chatInputMode, chatState.chatInputMode) &&
- Objects.equals(lastTypedText, chatState.lastTypedText) &&
- isChatInBottom == chatState.isChatInBottom &&
- engagementRequested == chatState.engagementRequested &&
- Objects.equals(pendingNavigationType, chatState.pendingNavigationType) &&
- Objects.equals(messagesNotSeen, chatState.messagesNotSeen) &&
- Objects.equals(operatorStatusItem, chatState.operatorStatusItem) &&
- Objects.equals(unsentMessages, chatState.unsentMessages) &&
- Objects.equals(chatItems, chatState.chatItems) &&
- showSendButton == chatState.showSendButton &&
- isOperatorTyping == chatState.isOperatorTyping &&
- isAttachmentButtonEnabled == chatState.isAttachmentButtonEnabled &&
- isAttachmentButtonNeeded == chatState.isAttachmentButtonNeeded &&
- isAttachmentAllowed == chatState.isAttachmentAllowed &&
- isSecureMessaging == chatState.isSecureMessaging;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(integratorChatStarted, isVisible, isChatInBottom, formattedOperatorName, operatorProfileImgUrl, companyName, queueId, visitorContextAssetId, mediaUpgradeStartedTimerItem, chatItems, chatInputMode, lastTypedText, messagesNotSeen, engagementRequested, pendingNavigationType, unsentMessages, showSendButton, isOperatorTyping, isAttachmentButtonEnabled, isAttachmentButtonNeeded);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "ChatState{" +
- "integratorChatStarted=" + integratorChatStarted +
- ", isVisible=" + isVisible +
- ", operatorName='" + formattedOperatorName + '\'' +
- ", operatorProfileImgUrl='" + operatorProfileImgUrl + '\'' +
- ", companyName='" + companyName + '\'' +
- ", queueId='" + queueId + '\'' +
- ", visitorContextAssetId='" + visitorContextAssetId + '\'' +
- ", mediaUpgradeStartedTimerItem=" + mediaUpgradeStartedTimerItem +
- ", chatInputMode=" + chatInputMode +
- ", lastTypedText: " + lastTypedText +
- ", messagesNotSeen: " + messagesNotSeen +
- ", isChatInBottom: " + isChatInBottom +
- ", engagementRequested: " + engagementRequested +
- ", pendingNavigationType: " + pendingNavigationType +
- ", operatorStatusItem: " + operatorStatusItem +
- ", unsentMessages: " + unsentMessages +
- ", chatItems=" + chatItems +
- ", showSendButton=" + showSendButton +
- ", isOperatorTyping=" + isOperatorTyping +
- ", isAttachmentButtonEnabled=" + isAttachmentButtonEnabled +
- ", isAttachmentButtonEnabled=" + isAttachmentButtonEnabled +
- ", isAttachmentAllowed=" + isAttachmentAllowed +
- ", isSecureMessaging=" + isSecureMessaging +
- '}';
- }
-
- public boolean isOperatorOnline() {
- return formattedOperatorName != null;
- }
-
- public boolean isMediaUpgradeStarted() {
- return mediaUpgradeStartedTimerItem != null;
- }
-
- public boolean isAudioCallStarted() {
- return isMediaUpgradeStarted() &&
- mediaUpgradeStartedTimerItem.type == MediaUpgradeStartedTimerItem.Type.AUDIO;
- }
-
- public boolean showMessagesUnseenIndicator() {
- return !isChatInBottom && messagesNotSeen != null && messagesNotSeen > 0;
- }
-
- public boolean isAttachmentButtonVisible() {
- return isAttachmentButtonNeeded && isAttachmentAllowed;
- }
-
- public ChatState initChat(String companyName,
- String queueId,
- String visitorContextAssetId) {
- return new Builder()
- .copyFrom(this)
- .setIntegratorChatStarted(true)
- .setCompanyName(companyName)
- .setQueueId(queueId)
- .setVisitorContextAssetId(visitorContextAssetId)
- .setIsVisible(true)
- .setShowSendButton(false)
- .setIsAttachmentButtonEnabled(true)
- .setIsAttachmentAllowed(true)
- .createChatState();
- }
-
- public String getFormattedOperatorName() {
- return formattedOperatorName;
- }
-
- public ChatState queueingStarted(OperatorStatusItem operatorStatusItem) {
- return new Builder()
- .copyFrom(this)
- .setFormattedOperatorName(null)
- .setOperatorProfileImgUrl(null)
- .setChatInputMode(ChatInputMode.ENABLED)
- .setEngagementRequested(true)
- .setOperatorStatusItem(operatorStatusItem)
- .createChatState();
- }
-
- public ChatState setSecureMessagingState() {
- return new Builder()
- .copyFrom(this)
- .setSecureMessaging(true)
- .enableChatPanel()
- .createChatState();
- }
-
- public ChatState setLiveChatState() {
- return new Builder()
- .copyFrom(this)
- .setSecureMessaging(false)
- .createChatState();
- }
-
- public ChatState allowSendAttachmentStateChanged(boolean isAttachmentAllowed) {
- return new Builder()
- .copyFrom(this)
- .setIsAttachmentAllowed(isAttachmentAllowed)
- .createChatState();
- }
-
- public ChatState engagementStarted() {
- return new Builder()
- .copyFrom(this)
- .enableChatPanel()
- .setEngagementRequested(true)
- .createChatState();
- }
-
- public ChatState transferring() {
- return new Builder()
- .copyFrom(this)
- .setFormattedOperatorName(null)
- .setOperatorProfileImgUrl(null)
- .setEngagementRequested(true)
- .setOperatorStatusItem(OperatorStatusItem.TransferringStatusItem())
- .disableChatPanel()
- .createChatState();
- }
-
- public ChatState operatorConnected(String formattedOperatorName, @Nullable String operatorProfileImgUrl) {
- return new Builder()
- .copyFrom(this)
- .setFormattedOperatorName(formattedOperatorName)
- .setOperatorProfileImgUrl(operatorProfileImgUrl)
- .enableChatPanel()
- .createChatState();
- }
-
- public ChatState historyLoaded(List chatItems) {
- return new Builder()
- .copyFrom(this)
- .setChatInputMode(ChatInputMode.ENABLED_NO_ENGAGEMENT)
- .setIsAttachmentButtonNeeded(false)
- .setChatItems(chatItems)
- .createChatState();
- }
-
- public ChatState changeItems(List newItems) {
- return new Builder()
- .copyFrom(this)
- .setChatItems(newItems)
- .createChatState();
- }
-
- public ChatState changeTimerItem(
- List newItems,
- MediaUpgradeStartedTimerItem mediaUpgradeStartedTimerItem
- ) {
- return new Builder()
- .copyFrom(changeItems(newItems))
- .setMediaUpgradeStartedItem(mediaUpgradeStartedTimerItem)
- .createChatState();
- }
-
- public ChatState changeVisibility(boolean isVisible) {
- return new Builder()
- .copyFrom(this)
- .setIsVisible(isVisible)
- .createChatState();
- }
-
- public ChatState setLastTypedText(String text) {
- return new Builder()
- .copyFrom(this)
- .setLastTypedText(text)
- .createChatState();
- }
-
- public ChatState chatInputModeChanged(ChatInputMode chatInputMode) {
- return new Builder()
- .copyFrom(this)
- .setChatInputMode(chatInputMode)
- .setIsAttachmentButtonNeeded(chatInputMode == ChatInputMode.ENABLED)
- .createChatState();
- }
-
- public ChatState isInBottomChanged(boolean isChatInBottom) {
- return new Builder()
- .copyFrom(this)
- .setIsChatInBottom(isChatInBottom)
- .createChatState();
- }
-
- public ChatState messagesNotSeenChanged(int messagesNotSeen) {
- return new Builder()
- .copyFrom(this)
- .setMessagesNotSeen(messagesNotSeen)
- .createChatState();
- }
-
- public ChatState setPendingNavigationType(String pendingNavigationType) {
- return new Builder()
- .copyFrom(this)
- .setPendingNavigationType(pendingNavigationType)
- .createChatState();
- }
-
- public ChatState changeUnsentMessages(List unsentMessages) {
- return new Builder()
- .copyFrom(this)
- .setUnsentMessages(Collections.unmodifiableList(unsentMessages))
- .createChatState();
- }
-
- public ChatState setShowSendButton(boolean isShow) {
- return new Builder()
- .copyFrom(this)
- .setShowSendButton(isShow)
- .createChatState();
- }
-
- public ChatState setIsOperatorTyping(boolean isOperatorTyping) {
- return new Builder()
- .copyFrom(this)
- .setIsOperatorTyping(isOperatorTyping)
- .createChatState();
- }
-
- public ChatState setIsAttachmentButtonEnabled(boolean isAttachmentButtonEnabled) {
- return new Builder()
- .copyFrom(this)
- .setIsAttachmentButtonEnabled(isAttachmentButtonEnabled)
- .createChatState();
- }
-
- public ChatState stop() {
- return new Builder()
- .copyFrom(this)
- .setFormattedOperatorName(null)
- .setOperatorProfileImgUrl(null)
- .setIsVisible(false)
- .setIntegratorChatStarted(false)
- .setIsAttachmentButtonNeeded(false)
- .createChatState();
- }
-
- public static class Builder {
- public boolean isOperatorTyping;
- public boolean isAttachmentButtonEnabled;
- public boolean isAttachmentButtonNeeded;
- private boolean isChatInBottom;
- private String formattedOperatorName;
- private String operatorProfileImgUrl;
- private String companyName;
- private String queueId;
- private boolean isVisible;
- private boolean integratorChatStarted;
- private MediaUpgradeStartedTimerItem mediaUpgradeStartedTimerItem;
- private List chatItems;
- private ChatInputMode chatInputMode;
- private String lastTypedText;
- private Integer messagesNotSeen;
- private boolean engagementRequested;
- private String pendingNavigationType;
- private List unsentMessages;
- private OperatorStatusItem operatorStatusItem;
- private boolean showSendButton;
- private boolean isAttachmentAllowed;
- private String visitorContextAssetId;
- private boolean isSecureMessaging;
-
- public Builder copyFrom(ChatState chatState) {
- isChatInBottom = chatState.isChatInBottom;
- formattedOperatorName = chatState.formattedOperatorName;
- operatorProfileImgUrl = chatState.operatorProfileImgUrl;
- companyName = chatState.companyName;
- queueId = chatState.queueId;
- visitorContextAssetId = chatState.visitorContextAssetId;
- isVisible = chatState.isVisible;
- integratorChatStarted = chatState.integratorChatStarted;
- mediaUpgradeStartedTimerItem = chatState.mediaUpgradeStartedTimerItem;
- chatItems = chatState.chatItems;
- chatInputMode = chatState.chatInputMode;
- lastTypedText = chatState.lastTypedText;
- messagesNotSeen = chatState.messagesNotSeen;
- engagementRequested = chatState.engagementRequested;
- pendingNavigationType = chatState.pendingNavigationType;
- unsentMessages = chatState.unsentMessages;
- operatorStatusItem = chatState.operatorStatusItem;
- showSendButton = chatState.showSendButton;
- isOperatorTyping = chatState.isOperatorTyping;
- isAttachmentButtonEnabled = chatState.isAttachmentButtonEnabled;
- isAttachmentButtonNeeded = chatState.isAttachmentButtonNeeded;
- isAttachmentAllowed = chatState.isAttachmentAllowed;
- isSecureMessaging = chatState.isSecureMessaging;
- return this;
- }
-
- public Builder setFormattedOperatorName(String formattedOperatorName) {
- this.formattedOperatorName = formattedOperatorName;
- return this;
- }
-
- public Builder setOperatorProfileImgUrl(String operatorProfileImgUrl) {
- this.operatorProfileImgUrl = operatorProfileImgUrl;
- return this;
- }
-
- public Builder setCompanyName(String companyName) {
- this.companyName = companyName;
- return this;
- }
-
- public Builder setQueueId(String queueId) {
- this.queueId = queueId;
- return this;
- }
-
- public Builder setIsVisible(boolean isVisible) {
- this.isVisible = isVisible;
- return this;
- }
-
- public Builder setIntegratorChatStarted(boolean integratorChatStarted) {
- this.integratorChatStarted = integratorChatStarted;
- return this;
- }
-
- public Builder setMediaUpgradeStartedItem(MediaUpgradeStartedTimerItem mediaUpgradeStartedItem) {
- this.mediaUpgradeStartedTimerItem = mediaUpgradeStartedItem;
- return this;
- }
-
- public Builder setChatItems(List chatItems) {
- this.chatItems = chatItems;
- return this;
- }
-
- public Builder setChatInputMode(ChatInputMode chatInputMode) {
- this.chatInputMode = chatInputMode;
- return this;
- }
-
- public Builder setLastTypedText(String lastTypedText) {
- this.lastTypedText = lastTypedText;
- return this;
- }
-
- public Builder setIsChatInBottom(boolean isChatInBottom) {
- this.isChatInBottom = isChatInBottom;
- return this;
- }
-
- public Builder setMessagesNotSeen(Integer messagesNotSeen) {
- this.messagesNotSeen = messagesNotSeen;
- return this;
- }
-
- public Builder setEngagementRequested(boolean engagementRequested) {
- this.engagementRequested = engagementRequested;
- return this;
- }
-
- public Builder setPendingNavigationType(String pendingNavigationType) {
- this.pendingNavigationType = pendingNavigationType;
- return this;
- }
-
- public Builder setUnsentMessages(List unsentMessages) {
- this.unsentMessages = unsentMessages;
- return this;
- }
-
- public Builder setOperatorStatusItem(OperatorStatusItem operatorStatusItem) {
- this.operatorStatusItem = operatorStatusItem;
- return this;
- }
-
- public Builder setShowSendButton(boolean isShow) {
- this.showSendButton = isShow;
- return this;
- }
-
- public Builder setIsOperatorTyping(boolean isOperatorTyping) {
- this.isOperatorTyping = isOperatorTyping;
- return this;
- }
-
- public Builder setIsAttachmentButtonEnabled(boolean isAttachmentButtonEnabled) {
- this.isAttachmentButtonEnabled = isAttachmentButtonEnabled;
- return this;
- }
-
- public Builder setIsAttachmentButtonNeeded(boolean isAttachmentButtonNeeded) {
- this.isAttachmentButtonNeeded = isAttachmentButtonNeeded;
- return this;
- }
-
- public Builder setIsAttachmentAllowed(boolean isAttachmentAllowed) {
- this.isAttachmentAllowed = isAttachmentAllowed;
- return this;
- }
-
- public ChatState createChatState() {
- return new ChatState(this);
- }
-
- public Builder setVisitorContextAssetId(String visitorContextAssetId) {
- this.visitorContextAssetId = visitorContextAssetId;
- return this;
- }
-
- public Builder setSecureMessaging(boolean secureMessaging) {
- this.isSecureMessaging = secureMessaging;
- return this;
- }
-
- public Builder enableChatPanel() {
- setChatInputMode(ChatInputMode.ENABLED);
- setIsAttachmentButtonNeeded(true);
- return this;
- }
-
- public Builder disableChatPanel() {
- setChatInputMode(ChatInputMode.DISABLED);
- setShowSendButton(false);
- setIsAttachmentButtonNeeded(false);
- return this;
- }
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt
new file mode 100644
index 000000000..8e7835839
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt
@@ -0,0 +1,124 @@
+package com.glia.widgets.chat.model
+
+internal data class ChatState(
+ val integratorChatStarted: Boolean = false,
+ val isVisible: Boolean = false,
+ val isChatInBottom: Boolean = true,
+ val messagesNotSeen: Int = 0,
+ val formattedOperatorName: String? = null,
+ val operatorProfileImgUrl: String? = null,
+ val companyName: String? = null,
+ val queueId: String? = null,
+ val visitorContextAssetId: String? = null,
+ val isMediaUpgradeVide: Boolean? = null,
+ val chatInputMode: ChatInputMode = ChatInputMode.ENABLED_NO_ENGAGEMENT,
+ val lastTypedText: String = "",
+ val engagementRequested: Boolean = false,
+ val pendingNavigationType: String? = null,
+ val operatorStatusItem: OperatorStatusItem? = null,
+ val showSendButton: Boolean = false,
+ val isAttachmentButtonEnabled: Boolean = false,
+ val isAttachmentButtonNeeded: Boolean = false,
+ val isOperatorTyping: Boolean = false,
+ val isAttachmentAllowed: Boolean = true,
+ val isSecureMessaging: Boolean = false,
+ val gvaQuickReplies: List = emptyList()
+) {
+
+ val isOperatorOnline: Boolean get() = formattedOperatorName != null
+
+ val isMediaUpgradeStarted: Boolean get() = isMediaUpgradeVide != null
+
+ val isAudioCallStarted: Boolean
+ get() = isMediaUpgradeVide != true
+
+ val showMessagesUnseenIndicator: Boolean get() = !isChatInBottom && messagesNotSeen > 0
+
+ val isAttachmentButtonVisible: Boolean get() = isAttachmentButtonNeeded && isAttachmentAllowed
+
+ fun initChat(companyName: String?, queueId: String?, visitorContextAssetId: String?): ChatState = copy(
+ integratorChatStarted = true,
+ companyName = companyName,
+ queueId = queueId,
+ visitorContextAssetId = visitorContextAssetId,
+ isVisible = true,
+ showSendButton = false,
+ isAttachmentButtonEnabled = true,
+ isAttachmentAllowed = true
+ )
+
+ fun queueingStarted(): ChatState = copy(
+ formattedOperatorName = null,
+ operatorProfileImgUrl = null,
+ chatInputMode = ChatInputMode.ENABLED,
+ engagementRequested = true,
+ )
+
+ fun setSecureMessagingState(): ChatState = copy(
+ isSecureMessaging = true,
+ chatInputMode = ChatInputMode.ENABLED,
+ isAttachmentButtonNeeded = true
+ )
+
+ fun setLiveChatState(): ChatState = copy(isSecureMessaging = false)
+
+ fun allowSendAttachmentStateChanged(isAttachmentAllowed: Boolean): ChatState = copy(isAttachmentAllowed = isAttachmentAllowed)
+
+ fun engagementStarted(): ChatState = copy(
+ chatInputMode = ChatInputMode.ENABLED,
+ isAttachmentButtonNeeded = true,
+ engagementRequested = true
+ )
+
+ fun transferring(): ChatState = copy(
+ formattedOperatorName = null,
+ operatorProfileImgUrl = null,
+ engagementRequested = false,
+ operatorStatusItem = OperatorStatusItem.Transferring,
+ chatInputMode = ChatInputMode.DISABLED,
+ showSendButton = false,
+ isAttachmentButtonNeeded = false
+ )
+
+ fun operatorConnected(
+ formattedOperatorName: String?,
+ operatorProfileImgUrl: String?
+ ): ChatState = copy(
+ formattedOperatorName = formattedOperatorName,
+ operatorProfileImgUrl = operatorProfileImgUrl,
+ chatInputMode = ChatInputMode.ENABLED,
+ isAttachmentButtonNeeded = true
+ )
+
+ fun historyLoaded(): ChatState = copy(
+ chatInputMode = ChatInputMode.ENABLED_NO_ENGAGEMENT,
+ isAttachmentButtonNeeded = false,
+ )
+
+ fun upgradeMedia(isVideo: Boolean?): ChatState = copy(isMediaUpgradeVide = isVideo)
+
+ fun changeVisibility(isVisible: Boolean): ChatState = copy(isVisible = isVisible)
+
+ fun setLastTypedText(text: String): ChatState = copy(lastTypedText = text)
+
+ fun isInBottomChanged(isChatInBottom: Boolean): ChatState = copy(isChatInBottom = isChatInBottom)
+
+ fun messagesNotSeenChanged(messagesNotSeen: Int): ChatState = copy(messagesNotSeen = messagesNotSeen)
+
+ fun setPendingNavigationType(pendingNavigationType: String?): ChatState = copy(pendingNavigationType = pendingNavigationType)
+
+ fun setShowSendButton(isShow: Boolean): ChatState = copy(showSendButton = isShow)
+
+ fun setIsOperatorTyping(isOperatorTyping: Boolean): ChatState = copy(isOperatorTyping = isOperatorTyping)
+
+ fun setIsAttachmentButtonEnabled(isAttachmentButtonEnabled: Boolean): ChatState = copy(isAttachmentButtonEnabled = isAttachmentButtonEnabled)
+
+ fun stop(): ChatState = copy(
+ formattedOperatorName = null,
+ operatorProfileImgUrl = null,
+ isVisible = false,
+ integratorChatStarted = false,
+ isAttachmentButtonNeeded = false,
+ isMediaUpgradeVide = null
+ )
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaBaseTypes.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaBaseTypes.kt
new file mode 100644
index 000000000..06c7b2cdb
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaBaseTypes.kt
@@ -0,0 +1,74 @@
+package com.glia.widgets.chat.model
+
+import android.net.Uri
+import androidx.annotation.StringDef
+import com.glia.androidsdk.chat.SingleChoiceAttachment
+import com.google.gson.annotations.SerializedName
+
+internal object Gva {
+
+ object Keys {
+ const val TYPE = "type"
+ const val CONTENT = "content"
+ const val OPTIONS = "options"
+ const val GALLERY_CARDS = "galleryCards"
+ }
+
+ enum class Type(val value: String) {
+ PLAIN_TEXT("plainText"),
+ PERSISTENT_BUTTONS("persistentButtons"),
+ QUICK_REPLIES("quickReplies"),
+ GALLERY_CARDS("galleryCards")
+ }
+
+ @StringDef(
+ UrlTarget.MODAL,
+ UrlTarget.SELF,
+ UrlTarget.BLANK
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class UrlTarget {
+ companion object {
+ const val MODAL = "modal"
+ const val SELF = "self"
+ const val BLANK = "blank"
+ }
+ }
+
+ sealed interface ButtonType {
+ object BroadcastEvent : ButtonType
+ data class PostBack(val singleChoiceAttachment: SingleChoiceAttachment) : ButtonType
+ data class Phone(val uri: Uri) : ButtonType
+ data class Email(val uri: Uri) : ButtonType
+ data class Url(val uri: Uri) : ButtonType
+ }
+}
+
+internal data class GvaButton(
+ @SerializedName("text")
+ val text: String = "",
+ @SerializedName("value")
+ val value: String = "",
+ @SerializedName("url")
+ val url: String? = null,
+ @Gva.UrlTarget
+ @SerializedName("urlTarget")
+ val urlTarget: String? = null,
+ @SerializedName("destinationPbBroadcastEvent")
+ val destinationPbBroadcastEvent: String? = null,
+ @SerializedName("transferPhoneNumber")
+ val transferPhoneNumber: String? = null
+) {
+ fun toResponse(): SingleChoiceAttachment = SingleChoiceAttachment.from(value, text)
+}
+
+internal data class GvaGalleryCard(
+ @SerializedName("title")
+ val title: String = "",
+ @SerializedName("subtitle")
+ val subtitle: String? = null,
+ @SerializedName("imageUrl")
+ val imageUrl: String? = null,
+ @SerializedName("options")
+ val options: List = listOf()
+)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaChatItems.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaChatItems.kt
new file mode 100644
index 000000000..b00360a80
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/GvaChatItems.kt
@@ -0,0 +1,61 @@
+package com.glia.widgets.chat.model
+
+import com.glia.widgets.chat.adapter.ChatAdapter
+
+internal abstract class GvaOperatorChatItem(@ChatAdapter.Type viewType: Int) : OperatorChatItem(viewType) {
+ abstract val operatorName: String?
+}
+
+internal data class GvaResponseText(
+ override val id: String = "",
+ val content: String = "",
+ override val showChatHead: Boolean = false,
+ override val operatorId: String? = "",
+ override val timestamp: Long = -1,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorName: String? = null
+) : GvaOperatorChatItem(ChatAdapter.GVA_RESPONSE_TEXT_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+}
+
+internal data class GvaPersistentButtons(
+ override val id: String = "",
+ val content: String = "",
+ val options: List = listOf(),
+ override val showChatHead: Boolean = false,
+ override val operatorId: String? = "",
+ override val timestamp: Long = -1,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorName: String? = null
+) : GvaOperatorChatItem(ChatAdapter.GVA_PERSISTENT_BUTTONS_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+}
+
+internal data class GvaGalleryCards(
+ override val id: String = "",
+ val galleryCards: List,
+ override val showChatHead: Boolean = false,
+ override val operatorId: String? = "",
+ override val timestamp: Long = -1,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorName: String? = null
+) : GvaOperatorChatItem(ChatAdapter.GVA_GALLERY_CARDS_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+}
+
+internal data class GvaQuickReplies(
+ override val id: String = "",
+ val content: String = "",
+ override val showChatHead: Boolean = false,
+ override val operatorId: String? = "",
+ override val timestamp: Long = -1,
+ override val operatorProfileImgUrl: String? = null,
+ override val operatorName: String? = null,
+ val options: List = listOf(),
+) : GvaOperatorChatItem(ChatAdapter.GVA_QUICK_REPLIES_TYPE) {
+ override fun withShowChatHead(showChatHead: Boolean): OperatorChatItem = copy(showChatHead = showChatHead)
+
+ fun asResponseText(): GvaResponseText = GvaResponseText(
+ id, content, showChatHead, operatorId, timestamp, operatorProfileImgUrl, operatorName
+ )
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ChatItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ChatItem.java
deleted file mode 100644
index 0dffcdc3f..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ChatItem.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class ChatItem {
- @ChatAdapter.Type
- private final int viewType;
- private final String id;
-
- protected ChatItem(String id, @ChatAdapter.Type int viewType) {
- this.id = id;
- this.viewType = viewType;
- }
-
- @ChatAdapter.Type
- public int getViewType() {
- return viewType;
- }
-
- public String getId() {
- return id;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ChatItem chatItem = (ChatItem) o;
- return viewType == chatItem.viewType &&
- id.equals(chatItem.id);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(viewType, id);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/CustomCardItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/CustomCardItem.java
deleted file mode 100644
index aef055a68..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/CustomCardItem.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.androidsdk.chat.ChatMessage;
-
-import java.util.Objects;
-
-public class CustomCardItem extends LinkedChatItem implements ServerChatItem {
- private final ChatMessage message;
-
- public CustomCardItem(ChatMessage message, int viewType) {
- super(message.getId(), viewType, message.getId(), message.getTimestamp());
-
- this.message = message;
- }
-
- public ChatMessage getMessage() {
- return message;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
-
- CustomCardItem that = (CustomCardItem) o;
-
- if (message.getTimestamp() != that.message.getTimestamp()) return false;
- if (!message.getId().equals(that.message.getId())) return false;
- if (!message.getContent().equals(that.message.getContent())) return false;
- if (message.getSenderType() != that.message.getSenderType()) return false;
- if (!Objects.equals(message.getAttachment(), that.message.getAttachment())) return false;
- return Objects.equals(message.getMetadata(), that.message.getMetadata());
- }
-
- @Override
- public int hashCode() {
- int result = (int) (message.getTimestamp() ^ (message.getTimestamp() >>> 32));
- result = 31 * result + message.getId().hashCode();
- result = 31 * result + message.getContent().hashCode();
- result = 31 * result + message.getSenderType().hashCode();
- result = 31 * result + (message.getAttachment() != null ? message.getAttachment().hashCode() : 0);
- result = 31 * result + (message.getMetadata() != null ? message.getMetadata().hashCode() : 0);
- return result;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "CustomCardItem{" +
- "message={" +
- "timestamp=" + message.getTimestamp() +
- ", id='" + message.getId() + '\'' +
- ", content='" + message.getContent() + '\'' +
- ", sender=" + message.getSenderType() +
- ", attachment=" + message.getAttachment() +
- ", metadata=" + message.getMetadata() +
- "}" +
- "}";
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/LinkedChatItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/LinkedChatItem.java
deleted file mode 100644
index eec4814db..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/LinkedChatItem.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-public class LinkedChatItem extends ChatItem {
-
- private final String messageId;
- private final long timestamp;
-
- public LinkedChatItem(String id, @ChatAdapter.Type int viewType, String messageId, long timestamp) {
- super(id, viewType);
- this.messageId = messageId;
- this.timestamp = timestamp;
- }
-
- public String getMessageId() {
- return messageId;
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/MediaUpgradeStartedTimerItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/MediaUpgradeStartedTimerItem.java
deleted file mode 100644
index 013e754c4..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/MediaUpgradeStartedTimerItem.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class MediaUpgradeStartedTimerItem extends ChatItem {
- public final static String ID = "media_upgrade_item";
- public final MediaUpgradeStartedTimerItem.Type type;
- public final String time;
-
- public MediaUpgradeStartedTimerItem(MediaUpgradeStartedTimerItem.Type type, String time) {
- super(ID, ChatAdapter.MEDIA_UPGRADE_ITEM_TYPE);
- this.type = type;
- this.time = time;
- }
-
- public enum Type {
- AUDIO, VIDEO
- }
-
- @Override
- public String toString() {
- return "MediaUpgradeStartedTimerItem{" +
- "type=" + type +
- ", time='" + time + '\'' +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
- MediaUpgradeStartedTimerItem that = (MediaUpgradeStartedTimerItem) o;
- return type == that.type &&
- Objects.equals(time, that.time);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), type, time);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/NewMessagesItem.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/NewMessagesItem.kt
deleted file mode 100644
index 33ab7060d..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/NewMessagesItem.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.glia.widgets.chat.model.history
-
-import com.glia.widgets.chat.adapter.ChatAdapter
-import java.util.*
-
-object NewMessagesItem : ChatItem(UUID.randomUUID().toString(), ChatAdapter.NEW_MESSAGES_DIVIDER_TYPE)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorAttachmentItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorAttachmentItem.java
deleted file mode 100644
index 1c13473e8..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorAttachmentItem.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.androidsdk.chat.AttachmentFile;
-import com.glia.widgets.helper.Utils;
-
-import java.util.Objects;
-
-public class OperatorAttachmentItem extends OperatorChatItem {
-
- public final AttachmentFile attachmentFile;
- public final boolean isFileExists;
- public final boolean isDownloading;
-
- public OperatorAttachmentItem(
- String chatItemId,
- int viewType,
- boolean showChatHead,
- AttachmentFile attachmentFile,
- String operatorProfileImgUrl,
- boolean isFileExists,
- boolean isDownloading,
- String operatorId,
- String messageId,
- long timestamp
- ) {
- super(chatItemId, viewType, showChatHead, operatorProfileImgUrl, operatorId, messageId, timestamp);
- this.attachmentFile = attachmentFile;
- this.isFileExists = isFileExists;
- this.isDownloading = isDownloading;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof OperatorAttachmentItem)) return false;
- if (!super.equals(o)) return false;
- OperatorAttachmentItem that = (OperatorAttachmentItem) o;
- return isFileExists == that.isFileExists && isDownloading == that.isDownloading && Objects.equals(attachmentFile, that.attachmentFile);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), attachmentFile, isFileExists, isDownloading);
- }
-
- @Override
- public String toString() {
- return "OperatorAttachmentItem{" +
- "attachmentFile=" + attachmentFile +
- ", isFileExists=" + isFileExists +
- ", isDownloading=" + isDownloading +
- ", showChatHead=" + showChatHead +
- ", operatorProfileImgUrl='" + operatorProfileImgUrl + '\'' +
- ", operatorId='" + operatorId + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorChatItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorChatItem.java
deleted file mode 100644
index 3d17cbc1c..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorChatItem.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import java.util.Objects;
-
-public abstract class OperatorChatItem extends LinkedChatItem implements ServerChatItem {
-
- public final boolean showChatHead;
- public final String operatorProfileImgUrl;
- public final String operatorId;
-
- protected OperatorChatItem(String id, int viewType, boolean showChatHead, String operatorProfileImgUrl, String operatorId, String messageId, long timestamp) {
- super(id, viewType, messageId, timestamp);
- this.showChatHead = showChatHead;
- this.operatorProfileImgUrl = operatorProfileImgUrl;
- this.operatorId = operatorId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof OperatorChatItem)) return false;
- if (!super.equals(o)) return false;
- OperatorChatItem that = (OperatorChatItem) o;
- return showChatHead == that.showChatHead
- && Objects.equals(operatorProfileImgUrl, that.operatorProfileImgUrl)
- && Objects.equals(operatorId, that.operatorId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), showChatHead, operatorProfileImgUrl, operatorId);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorMessageItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorMessageItem.java
deleted file mode 100644
index 3efe2a5ca..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorMessageItem.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class OperatorMessageItem extends OperatorChatItem {
- public final String operatorName;
- public final String content;
-
- public OperatorMessageItem(
- String id,
- String operatorName,
- String operatorProfileImgUrl,
- boolean showChatHead,
- String content,
- String operatorId,
- long timestamp
- ) {
- super(id, ChatAdapter.OPERATOR_MESSAGE_VIEW_TYPE, showChatHead, operatorProfileImgUrl, operatorId, id, timestamp);
- this.operatorName = operatorName;
- this.content = content;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof OperatorMessageItem)) return false;
- if (!super.equals(o)) return false;
- OperatorMessageItem that = (OperatorMessageItem) o;
- return Objects.equals(operatorName, that.operatorName)
- && Objects.equals(content, that.content);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), operatorName, content);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "OperatorMessageItem{" +
- "showChatHead=" + showChatHead +
- ", operatorProfileImgUrl='" + operatorProfileImgUrl + '\'' +
- ", operatorId='" + operatorId + '\'' +
- ", operatorName='" + operatorName + '\'' +
- ", content='" + content + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorStatusItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorStatusItem.java
deleted file mode 100644
index 1f6fac7e0..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/OperatorStatusItem.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class OperatorStatusItem extends ChatItem {
- public static final String ID = "operator_status_item";
- private final String companyName;
- private final Status status;
- private final String operatorName;
- private final String profileImgUrl;
-
- public OperatorStatusItem(Status status, String companyName, String operatorName, String profileImgUrl) {
- super(ID, ChatAdapter.OPERATOR_STATUS_VIEW_TYPE);
- this.companyName = companyName;
- this.status = status;
- this.operatorName = operatorName;
- this.profileImgUrl = profileImgUrl;
- }
-
- public String getCompanyName() {
- return companyName;
- }
-
- @NonNull
- public Status getStatus() {
- return status;
- }
-
- public String getOperatorName() {
- return operatorName;
- }
-
- public String getProfileImgUrl() {
- return profileImgUrl;
- }
-
- public enum Status {
- IN_QUEUE,
- OPERATOR_CONNECTED,
- JOINED,
- TRANSFERRING
- }
-
- public static OperatorStatusItem QueueingStatusItem(String companyName) {
- return new OperatorStatusItem(OperatorStatusItem.Status.IN_QUEUE, companyName, null, null);
- }
-
- public static OperatorStatusItem OperatorFoundStatusItem(String companyName, String operatorName, String profileImgUrl) {
- return new OperatorStatusItem(Status.OPERATOR_CONNECTED, companyName, operatorName, profileImgUrl);
- }
-
- public static OperatorStatusItem OperatorJoinedStatusItem(String companyName, String operatorName, String profileImgUrl) {
- return new OperatorStatusItem(Status.JOINED, companyName, operatorName, profileImgUrl);
- }
-
- public static OperatorStatusItem TransferringStatusItem() {
- return new OperatorStatusItem(Status.TRANSFERRING, null, null, null);
- }
-
- @Override
- public String toString() {
- return "OperatorStatusItem{" +
- "companyName='" + companyName + '\'' +
- ", status=" + status +
- ", operatorName='" + operatorName + '\'' +
- ", profileImgUrl='" + profileImgUrl + '\'' +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
- OperatorStatusItem that = (OperatorStatusItem) o;
- return Objects.equals(companyName, that.companyName) &&
- status == that.status &&
- Objects.equals(operatorName, that.operatorName) &&
- Objects.equals(profileImgUrl, that.profileImgUrl);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), companyName, status, operatorName, profileImgUrl);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ResponseCardItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ResponseCardItem.java
deleted file mode 100644
index d4985ae8f..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ResponseCardItem.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.androidsdk.chat.SingleChoiceOption;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class ResponseCardItem extends OperatorMessageItem {
- @NonNull
- public final List singleChoiceOptions;
- public final String choiceCardImageUrl;
-
- public ResponseCardItem(
- @NonNull String id,
- String operatorName,
- String operatorProfileImgUrl,
- boolean showChatHead,
- String content,
- String operatorId,
- long timestamp,
- //Must not be empty, otherwise it is OperatorMessageItem
- @NonNull List singleChoiceOptions,
- String choiceCardImageUrl
- ) {
- super(id, operatorName, operatorProfileImgUrl, showChatHead, content, operatorId, timestamp);
-
- assert !singleChoiceOptions.isEmpty();
- this.singleChoiceOptions = Collections.unmodifiableList(singleChoiceOptions);
- this.choiceCardImageUrl = choiceCardImageUrl;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof ResponseCardItem)) return false;
- if (!super.equals(o)) return false;
- ResponseCardItem that = (ResponseCardItem) o;
- return singleChoiceOptions.equals(that.singleChoiceOptions) && Objects.equals(choiceCardImageUrl, that.choiceCardImageUrl);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), singleChoiceOptions, choiceCardImageUrl);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "ResponseCardItem{" +
- "singleChoiceOptions=" + singleChoiceOptions +
- ", choiceCardImageUrl='" + choiceCardImageUrl + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ServerChatItem.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ServerChatItem.kt
deleted file mode 100644
index 883a058ba..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/ServerChatItem.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.glia.widgets.chat.model.history
-
-/**
- * Indicates that the [ChatItem] was sent by server
- */
-internal interface ServerChatItem
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/SystemChatItem.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/SystemChatItem.kt
deleted file mode 100644
index dd346d054..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/SystemChatItem.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.glia.widgets.chat.model.history
-
-import com.glia.widgets.chat.adapter.ChatAdapter
-
-class SystemChatItem(id: String, timestamp: Long, val message: String) :
- LinkedChatItem(id, ChatAdapter.SYSTEM_MESSAGE_TYPE, id, timestamp), ServerChatItem
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorAttachmentItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorAttachmentItem.java
deleted file mode 100644
index 1bff7203f..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorAttachmentItem.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.androidsdk.chat.AttachmentFile;
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class VisitorAttachmentItem extends LinkedChatItem {
-
- public final AttachmentFile attachmentFile;
- public final boolean isFileExists;
- public final boolean isDownloading;
- public final boolean showDelivered;
-
- private VisitorAttachmentItem(String chatItemId, int viewType, AttachmentFile attachmentFile,
- boolean isFileExists, boolean isDownloading, boolean showDelivered, long timestamp) {
- super(chatItemId, viewType, chatItemId, timestamp);
- this.attachmentFile = attachmentFile;
- this.isFileExists = isFileExists;
- this.isDownloading = isDownloading;
- this.showDelivered = showDelivered;
- }
-
- public static VisitorAttachmentItem editDeliveredStatus(VisitorAttachmentItem source, boolean isDelivered) {
- return new VisitorAttachmentItem(
- source.getId(),
- source.getViewType(),
- source.attachmentFile,
- source.isFileExists,
- source.isDownloading,
- isDelivered,
- source.getTimestamp()
- );
- }
-
- public static VisitorAttachmentItem editDownloadedStatus(VisitorAttachmentItem source, boolean isDownloaded) {
- return new VisitorAttachmentItem(
- source.getId(),
- source.getViewType(),
- source.attachmentFile,
- isDownloaded,
- source.isDownloading,
- source.showDelivered,
- source.getTimestamp()
- );
- }
-
- public static VisitorAttachmentItem editFileStatuses(VisitorAttachmentItem source, boolean doesFileExists, boolean isDownloading) {
- return new VisitorAttachmentItem(
- source.getId(),
- source.getViewType(),
- source.attachmentFile,
- doesFileExists,
- isDownloading,
- source.showDelivered,
- source.getTimestamp()
- );
- }
-
- public static VisitorAttachmentItem fromAttachmentFile(String messageId, long messageTimestamp, AttachmentFile file) {
- int type;
- if (file.getContentType().startsWith("image")) {
- type = ChatAdapter.VISITOR_IMAGE_VIEW_TYPE;
- } else {
- type = ChatAdapter.VISITOR_FILE_VIEW_TYPE;
- }
- return new VisitorAttachmentItem(messageId, type, file, false, false, false, messageTimestamp);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
- VisitorAttachmentItem that = (VisitorAttachmentItem) o;
- return isFileExists == that.isFileExists && isDownloading == that.isDownloading &&
- showDelivered == that.showDelivered && Objects.equals(attachmentFile, that.attachmentFile);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), attachmentFile, isFileExists, isDownloading, showDelivered);
- }
-
- @NonNull
- @Override
- public String toString() {
- return "VisitorAttachmentItem{" +
- "attachmentFile=" + attachmentFile +
- ", chatItemId=" + getId() +
- ", isFileExists=" + isFileExists +
- ", isDownloading=" + isDownloading +
- ", showDelivered=" + showDelivered +
- '}';
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorMessageItem.java b/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorMessageItem.java
deleted file mode 100644
index 98c7fd862..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/history/VisitorMessageItem.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.glia.widgets.chat.model.history;
-
-import androidx.annotation.NonNull;
-
-import com.glia.androidsdk.chat.ChatMessage;
-import com.glia.androidsdk.chat.SingleChoiceAttachment;
-import com.glia.widgets.chat.adapter.ChatAdapter;
-
-import java.util.Objects;
-
-public class VisitorMessageItem extends LinkedChatItem {
- public final static String HISTORY_ID = "history_id";
- public final static String CARD_RESPONSE_ID = "card_response_id";
- public final static String UNSENT_MESSAGE_ID = "unsent_message_id";
- private final boolean showDelivered;
- private final String message;
-
- private VisitorMessageItem(String id, String messageId, String message, long timestamp, boolean showDelivered) {
- super(id, ChatAdapter.VISITOR_MESSAGE_TYPE, messageId, timestamp);
- this.showDelivered = showDelivered;
- this.message = message;
- }
-
- public static VisitorMessageItem asNewMessage(ChatMessage message) {
- return new VisitorMessageItem(message.getId(), message.getId(), message.getContent(), message.getTimestamp(), false);
- }
-
- public static VisitorMessageItem asUnsentItem(String unsentMessageText) {
- return new VisitorMessageItem(UNSENT_MESSAGE_ID, null, unsentMessageText, System.currentTimeMillis(), false);
- }
-
- public static VisitorMessageItem asHistoryItem(ChatMessage message) {
- return new VisitorMessageItem(HISTORY_ID, message.getId(), message.getContent(), message.getTimestamp(), false);
- }
-
- public static VisitorMessageItem asCardResponseItem(ChatMessage message) {
- String selectedOptionText = null;
- if (message.getAttachment() != null && message.getAttachment() instanceof SingleChoiceAttachment) {
- SingleChoiceAttachment singleChoiceAttachment = (SingleChoiceAttachment) message.getAttachment();
- selectedOptionText = singleChoiceAttachment.getSelectedOptionText();
- }
- return new VisitorMessageItem(CARD_RESPONSE_ID, message.getId(), selectedOptionText, message.getTimestamp(), false);
- }
-
- public static VisitorMessageItem asUnsentCardResponse(String unsentResponse) {
- return new VisitorMessageItem(CARD_RESPONSE_ID, null, unsentResponse, System.currentTimeMillis(), false);
- }
-
- public static VisitorMessageItem editDeliveredStatus(VisitorMessageItem source, boolean showDelivered) {
- return new VisitorMessageItem(source.getId(), source.getMessageId(), source.getMessage(), source.getTimestamp(), showDelivered);
- }
-
- public String getMessage() {
- return message;
- }
-
- public boolean isShowDelivered() {
- return showDelivered;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "VisitorMessageItem{" +
- "chatItemId='" + getId() + '\'' +
- ", showDelivered=" + showDelivered +
- ", message='" + message + '\'' +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
- VisitorMessageItem that = (VisitorMessageItem) o;
- return showDelivered == that.showDelivered &&
- getId().equals(that.getId()) &&
- Objects.equals(message, that.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), getId(), showDelivered, message);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementStateRepository.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementStateRepository.java
index f6df6976f..00bc0b691 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementStateRepository.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementStateRepository.java
@@ -1,6 +1,7 @@
package com.glia.widgets.core.engagement;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.glia.androidsdk.Engagement;
import com.glia.androidsdk.Operator;
@@ -17,23 +18,22 @@
public class GliaEngagementStateRepository {
private final EngagementStateEventVisitor visitor = new EngagementStateEventVisitor.OperatorVisitor();
private final BehaviorProcessor> operatorProcessor =
- BehaviorProcessor.createDefault(Optional.empty());
+ BehaviorProcessor.createDefault(Optional.empty());
private final Flowable operatorFlowable =
- operatorProcessor
- .filter(Optional::isPresent)
- .map(Optional::get)
- .onBackpressureLatest();
+ operatorProcessor
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .onBackpressureLatest();
private final BehaviorProcessor> engagementStateProcessor = BehaviorProcessor.createDefault(Optional.empty());
private final BehaviorProcessor engagementStateEventProcessor = BehaviorProcessor.createDefault(
- new EngagementStateEvent.EngagementEndedEvent()
+ new EngagementStateEvent.NoEngagementEvent()
);
private final Flowable engagementStateEventFlowable = engagementStateEventProcessor.onBackpressureLatest();
-
- private CompositeDisposable disposable = new CompositeDisposable();
-
private final GliaOperatorRepository operatorRepository;
+ private CompositeDisposable disposable = new CompositeDisposable();
+ private boolean isOngoingEngagement = false;
public GliaEngagementStateRepository(GliaOperatorRepository operatorRepository) {
this.operatorRepository = operatorRepository;
@@ -42,14 +42,16 @@ public GliaEngagementStateRepository(GliaOperatorRepository operatorRepository)
public void onEngagementStarted(Engagement engagement) {
disposable = new CompositeDisposable();
disposable.add(
- engagementStateProcessor
- .onBackpressureLatest()
- .map(state -> mapToEngagementStateChangeEvent(state.orElse(null), getOperator()))
- .doOnNext(this::notifyEngagementStateEventUpdate)
- .doOnNext(this::updateOperatorOnEngagementStateChanged)
- .subscribe()
+ engagementStateProcessor
+ .onBackpressureLatest()
+ .map(state -> mapToEngagementStateChangeEvent(state.orElse(null), getOperator()))
+ .doOnNext(this::notifyEngagementStateEventUpdate)
+ .doOnNext(this::updateOperatorOnEngagementStateChanged)
+ .subscribe()
);
- engagement.on(Engagement.Events.STATE_UPDATE, this::notifyEngagementStateUpdate);
+ engagement.on(Engagement.Events.STATE_UPDATE, engagementState -> {
+ notifyEngagementStateUpdate(engagementState);
+ });
engagement.on(Engagement.Events.END, () -> onEngagementEnded(engagement));
}
@@ -75,7 +77,7 @@ public boolean isOperatorPresent() {
private void notifyOperatorUpdate(Operator operator) {
if (operator != null) {
- operatorRepository.addOrUpdateOperator(operator);
+ operatorRepository.emit(operator);
}
operatorProcessor.onNext(Optional.ofNullable(operator));
}
@@ -91,20 +93,25 @@ private void notifyEngagementStateEventUpdate(EngagementStateEvent engagementSta
private @Nullable
Operator getOperator() {
return operatorProcessor
- .getValue()
- .orElse(null);
+ .getValue()
+ .orElse(null);
}
- private EngagementStateEvent mapToEngagementStateChangeEvent(
+ @VisibleForTesting
+ protected EngagementStateEvent mapToEngagementStateChangeEvent(
EngagementState engagementState,
@Nullable Operator operator
) {
- if (engagementState == null) {
+ if (engagementState == null && isOngoingEngagement) {
+ isOngoingEngagement = false;
return new EngagementStateEvent.EngagementEndedEvent();
+ } else if (engagementState == null) {
+ return new EngagementStateEvent.NoEngagementEvent();
} else if (engagementState.getVisitorStatus() == EngagementState.VisitorStatus.TRANSFERRING) {
return new EngagementStateEvent.EngagementTransferringEvent();
} else {
if (operator == null) {
+ isOngoingEngagement = true;
return new EngagementStateEvent.EngagementOperatorConnectedEvent(engagementState.getOperator());
} else {
if (!engagementState.getOperator().getId().equals(operator.getId())) {
@@ -124,6 +131,7 @@ private void updateOperatorOnEngagementStateChanged(EngagementStateEvent engagem
notifyOperatorUpdate(visitor.visit(engagementStateEvent));
break;
case ENGAGEMENT_TRANSFERRING:
+ case NO_ENGAGEMENT:
case ENGAGEMENT_ENDED:
notifyOperatorUpdate(null);
break;
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.java
deleted file mode 100644
index c58b81f8f..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.glia.widgets.core.engagement;
-
-import androidx.annotation.NonNull;
-import androidx.collection.SimpleArrayMap;
-import androidx.core.util.Consumer;
-
-import com.glia.androidsdk.Operator;
-import com.glia.widgets.di.GliaCore;
-
-public class GliaOperatorRepository {
- private final GliaCore gliaCore;
-
- private final SimpleArrayMap cachedOperators = new SimpleArrayMap<>();
-
- public GliaOperatorRepository(GliaCore gliaCore) {
- this.gliaCore = gliaCore;
- }
-
- public void getOperatorById(@NonNull String operatorId, @NonNull Consumer callback) {
- Operator cachedOperator = cachedOperators.get(operatorId);
- if (cachedOperator != null) {
- callback.accept(cachedOperator);
- return;
- }
-
- gliaCore.getOperator(operatorId, (operator, error) -> {
- if (operator != null) {
- addOrUpdateOperator(operator);
- }
- callback.accept(operator);
- });
- }
-
- public void addOrUpdateOperator(@NonNull Operator operator) {
- cachedOperators.put(operator.getId(), operator);
- }
-
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.kt
new file mode 100644
index 000000000..c4d0abb32
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaOperatorRepository.kt
@@ -0,0 +1,42 @@
+package com.glia.widgets.core.engagement
+
+import androidx.annotation.VisibleForTesting
+import androidx.collection.SimpleArrayMap
+import androidx.core.util.Consumer
+import com.glia.androidsdk.Operator
+import com.glia.widgets.core.engagement.data.LocalOperator
+import com.glia.widgets.di.GliaCore
+import com.glia.widgets.helper.imageUrl
+
+internal class GliaOperatorRepository(private val gliaCore: GliaCore) {
+ private val cachedOperators = SimpleArrayMap()
+
+ @VisibleForTesting
+ var operatorDefaultImageUrl: String? = null
+
+ fun getOperatorById(operatorId: String, callback: Consumer) {
+ val cachedOperator = cachedOperators[operatorId]
+ if (cachedOperator != null) {
+ callback.accept(cachedOperator)
+ return
+ }
+ gliaCore.getOperator(operatorId) { operator: Operator?, _ ->
+ operator?.let { mapOperator(it) }?.also { putOperator(it) }.also { callback.accept(it) }
+ }
+ }
+
+ fun emit(operator: Operator) = putOperator(mapOperator(operator))
+
+ @VisibleForTesting
+ fun mapOperator(operator: Operator): LocalOperator = operator.run { LocalOperator(id, name, imageUrl ?: operatorDefaultImageUrl) }
+
+ @VisibleForTesting
+ fun putOperator(operator: LocalOperator) {
+ operator.apply { cachedOperators.put(id, this) }
+ }
+
+ fun updateOperatorDefaultImageUrl(imageUrl: String?) {
+ operatorDefaultImageUrl = imageUrl
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/data/LocalOperator.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/data/LocalOperator.kt
new file mode 100644
index 000000000..d5e515762
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/data/LocalOperator.kt
@@ -0,0 +1,3 @@
+package com.glia.widgets.core.engagement.data
+
+internal data class LocalOperator(val id: String, val name: String, val imageUrl: String?)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GetOperatorUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GetOperatorUseCase.java
index 9b3076069..dc63d30a5 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GetOperatorUseCase.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GetOperatorUseCase.java
@@ -2,8 +2,8 @@
import androidx.annotation.NonNull;
-import com.glia.androidsdk.Operator;
import com.glia.widgets.core.engagement.GliaOperatorRepository;
+import com.glia.widgets.core.engagement.data.LocalOperator;
import java.util.Optional;
@@ -16,11 +16,11 @@ public GetOperatorUseCase(GliaOperatorRepository gliaOperatorRepository) {
this.gliaOperatorRepository = gliaOperatorRepository;
}
- public Single> execute(@NonNull String operatorId) {
+ public Single> execute(@NonNull String operatorId) {
return Single.create(emitter ->
- gliaOperatorRepository.getOperatorById(operatorId, operator ->
- emitter.onSuccess(Optional.ofNullable(operator))
- )
+ gliaOperatorRepository.getOperatorById(operatorId, operator ->
+ emitter.onSuccess(Optional.ofNullable(operator))
+ )
);
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GliaOnEngagementUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GliaOnEngagementUseCase.java
index 82bc59b57..8be154da2 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GliaOnEngagementUseCase.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GliaOnEngagementUseCase.java
@@ -3,6 +3,7 @@
import com.glia.androidsdk.omnicore.OmnicoreEngagement;
import com.glia.widgets.core.engagement.GliaEngagementRepository;
import com.glia.widgets.core.engagement.GliaEngagementStateRepository;
+import com.glia.widgets.core.engagement.GliaOperatorRepository;
import com.glia.widgets.core.operator.GliaOperatorMediaRepository;
import com.glia.widgets.core.queue.GliaQueueRepository;
import com.glia.widgets.core.visitor.GliaVisitorMediaRepository;
@@ -11,29 +12,28 @@
public class GliaOnEngagementUseCase implements Consumer {
- public interface Listener {
- void newEngagementLoaded(OmnicoreEngagement engagement);
- }
-
private final GliaEngagementRepository gliaRepository;
private final GliaOperatorMediaRepository operatorMediaRepository;
private final GliaQueueRepository gliaQueueRepository;
private final GliaVisitorMediaRepository gliaVisitorMediaRepository;
private final GliaEngagementStateRepository gliaEngagementStateRepository;
+ private final GliaOperatorRepository operatorRepository;
private Listener listener;
public GliaOnEngagementUseCase(
- GliaEngagementRepository gliaRepository,
- GliaOperatorMediaRepository operatorMediaRepository,
- GliaQueueRepository gliaQueueRepository,
- GliaVisitorMediaRepository gliaVisitorMediaRepository,
- GliaEngagementStateRepository gliaEngagementStateRepository
+ GliaEngagementRepository gliaRepository,
+ GliaOperatorMediaRepository operatorMediaRepository,
+ GliaQueueRepository gliaQueueRepository,
+ GliaVisitorMediaRepository gliaVisitorMediaRepository,
+ GliaEngagementStateRepository gliaEngagementStateRepository,
+ GliaOperatorRepository operatorRepository
) {
this.gliaRepository = gliaRepository;
this.operatorMediaRepository = operatorMediaRepository;
this.gliaQueueRepository = gliaQueueRepository;
this.gliaVisitorMediaRepository = gliaVisitorMediaRepository;
this.gliaEngagementStateRepository = gliaEngagementStateRepository;
+ this.operatorRepository = operatorRepository;
}
public void execute(Listener listener) {
@@ -47,6 +47,7 @@ public void execute(Listener listener) {
@Override
public void accept(OmnicoreEngagement engagement) {
+ operatorRepository.emit(engagement.getState().getOperator());
operatorMediaRepository.onEngagementStarted(engagement);
gliaVisitorMediaRepository.onEngagementStarted(engagement);
gliaEngagementStateRepository.onEngagementStarted(engagement);
@@ -62,4 +63,8 @@ public void unregisterListener(Listener listener) {
this.listener = null;
}
}
+
+ public interface Listener {
+ void newEngagementLoaded(OmnicoreEngagement engagement);
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/MapOperatorUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/MapOperatorUseCase.kt
index 651c7b335..555784202 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/MapOperatorUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/MapOperatorUseCase.kt
@@ -1,28 +1,24 @@
package com.glia.widgets.core.engagement.domain
-import com.glia.androidsdk.Operator
import com.glia.androidsdk.chat.Chat
import com.glia.androidsdk.chat.ChatMessage
import com.glia.androidsdk.chat.OperatorMessage
import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
+import com.glia.widgets.helper.toChatMessageInternal
import io.reactivex.Single
+import kotlin.jvm.optionals.getOrNull
internal class MapOperatorUseCase(private val getOperatorUseCase: GetOperatorUseCase) {
operator fun invoke(chatMessage: ChatMessage): Single =
when (chatMessage.senderType) {
- Chat.Participant.OPERATOR -> Single.just(chatMessage)
- .cast(OperatorMessage::class.java)
- .flatMap { mapOperator(it) }
-
- else -> Single.just(chatMessage).map { ChatMessageInternal(it) }
+ Chat.Participant.OPERATOR -> processOperatorMessage(chatMessage as OperatorMessage)
+ else -> processVisitorMessage(chatMessage)
}
- private fun mapOperator(operatorMessage: OperatorMessage): Single {
- return getOperatorUseCase.execute(operatorMessage.operatorId!!)
- .map { map(operatorMessage, it.orElse(null)) }
- }
+ private fun processOperatorMessage(chatMessage: OperatorMessage): Single = chatMessage
+ .takeIf { it.operatorImageUrl != null }?.toChatMessageInternal()?.let { Single.just(it) }
+ ?: getOperatorUseCase.execute(chatMessage.operatorId!!).map { ChatMessageInternal(chatMessage, it.getOrNull()) }
+
+ private fun processVisitorMessage(chatMessage: ChatMessage): Single = Single.just(ChatMessageInternal(chatMessage))
- private fun map(operatorMessage: OperatorMessage, operator: Operator?): ChatMessageInternal {
- return ChatMessageInternal(operatorMessage, operator)
- }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/UpdateOperatorDefaultImageUrlUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/UpdateOperatorDefaultImageUrlUseCase.kt
new file mode 100644
index 000000000..9e57381e6
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/UpdateOperatorDefaultImageUrlUseCase.kt
@@ -0,0 +1,14 @@
+package com.glia.widgets.core.engagement.domain
+
+import com.glia.widgets.chat.domain.SiteInfoUseCase
+import com.glia.widgets.core.engagement.GliaOperatorRepository
+import kotlin.jvm.optionals.getOrNull
+
+internal class UpdateOperatorDefaultImageUrlUseCase(
+ private val operatorRepository: GliaOperatorRepository,
+ private val siteInfoUseCase: SiteInfoUseCase
+) {
+ operator fun invoke() = siteInfoUseCase.execute { response, _ ->
+ response.defaultOperatorPicture?.url?.getOrNull()?.also(operatorRepository::updateOperatorDefaultImageUrl)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.java
deleted file mode 100644
index 34ea8cf19..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.glia.widgets.core.engagement.domain.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.glia.androidsdk.Operator;
-import com.glia.androidsdk.chat.Chat;
-import com.glia.androidsdk.chat.ChatMessage;
-
-import java.util.Optional;
-
-public class ChatMessageInternal {
- @NonNull
- private final ChatMessage chatMessage;
- @Nullable
- private final Operator operator;
-
- public ChatMessageInternal(@NonNull ChatMessage chatMessage, @Nullable Operator operator) {
- this.chatMessage = chatMessage;
- this.operator = operator;
- }
-
- public ChatMessageInternal(ChatMessage chatMessage) {
- this(chatMessage, null);
- }
-
- @NonNull
- public ChatMessage getChatMessage() {
- return chatMessage;
- }
-
- public Optional getOperator() {
- return Optional.ofNullable(operator);
- }
-
- public Optional getOperatorId() {
- return getOperator().map(Operator::getId);
- }
-
- public Optional getOperatorName() {
- return getOperator().map(Operator::getName);
- }
-
- public Optional getOperatorImageUrl() {
- return getOperator().map(Operator::getPicture).flatMap(Operator.Picture::getURL);
- }
-
- public boolean isNotVisitor() {
- return getChatMessage().getSenderType() != Chat.Participant.VISITOR;
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.kt
new file mode 100644
index 000000000..55aa0e97b
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatMessageInternal.kt
@@ -0,0 +1,12 @@
+package com.glia.widgets.core.engagement.domain.model
+
+import com.glia.androidsdk.chat.Chat
+import com.glia.androidsdk.chat.ChatMessage
+import com.glia.widgets.core.engagement.data.LocalOperator
+
+internal data class ChatMessageInternal(val chatMessage: ChatMessage, val operator: LocalOperator? = null) {
+ val operatorId: String? get() = operator?.id
+ val operatorName: String? get() = operator?.name
+ val operatorImageUrl: String? get() = operator?.imageUrl
+ val isNotVisitor: Boolean get() = chatMessage.senderType != Chat.Participant.VISITOR
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/EngagementStateEvent.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/EngagementStateEvent.java
index 768973b0b..26a272398 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/EngagementStateEvent.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/EngagementStateEvent.java
@@ -9,6 +9,7 @@ enum Type {
ENGAGEMENT_ONGOING,
ENGAGEMENT_OPERATOR_CONNECTED,
ENGAGEMENT_OPERATOR_CHANGED,
+ NO_ENGAGEMENT
}
Type getType();
@@ -94,6 +95,7 @@ public T accept(EngagementStateEventVisitor visitor) {
}
class EngagementEndedEvent implements EngagementStateEvent {
+
@Override
public Type getType() {
return Type.ENGAGEMENT_ENDED;
@@ -104,6 +106,19 @@ public T accept(EngagementStateEventVisitor visitor) {
return visitor.visit(this);
}
}
+
+ class NoEngagementEvent implements EngagementStateEvent {
+
+ @Override
+ public Type getType() {
+ return Type.NO_ENGAGEMENT;
+ }
+
+ @Override
+ public T accept(EngagementStateEventVisitor visitor) {
+ return visitor.visit(this);
+ }
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
index 38640f4bb..802bed81a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
@@ -11,7 +11,9 @@ import com.glia.widgets.core.engagement.exception.EngagementMissingException
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase
import com.glia.widgets.core.fileupload.model.FileAttachment
import com.glia.widgets.di.GliaCore
-import java.util.*
+import java.util.Observable
+import java.util.Observer
+import kotlin.jvm.optionals.getOrNull
class FileAttachmentRepository(
private val gliaCore: GliaCore,
@@ -45,7 +47,7 @@ class FileAttachmentRepository(
}
fun uploadFile(file: FileAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
- val engagement = gliaCore.currentEngagement.orElse(null)
+ val engagement = gliaCore.currentEngagement.getOrNull()
if (engagement != null) {
engagement.uploadFile(file.uri, handleFileUpload(file, listener))
} else if (engagementConfigRepository.chatType == ChatType.SECURE_MESSAGING) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt
index 52ed196a1..571c8735c 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt
@@ -10,6 +10,7 @@ import com.glia.widgets.core.fileupload.model.FileAttachment
import com.glia.widgets.di.GliaCore
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
+import kotlin.jvm.optionals.getOrNull
class SecureFileAttachmentRepository(private val gliaCore: GliaCore) {
private val secureConversations: SecureConversations by lazy {
@@ -43,7 +44,7 @@ class SecureFileAttachmentRepository(private val gliaCore: GliaCore) {
}
fun uploadFile(file: FileAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
- val engagement = gliaCore.currentEngagement.orElse(null)
+ val engagement = gliaCore.currentEngagement.getOrNull()
if (engagement != null) {
engagement.uploadFile(file.uri, handleFileUpload(file, listener))
} else {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
index 3af8947c8..b7b7e3625 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
@@ -11,7 +11,7 @@ import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
-class SecureConversationsRepository(private val secureConversations: SecureConversations) {
+internal class SecureConversationsRepository(private val secureConversations: SecureConversations) {
private val _messageSendingObservable: Subject = BehaviorSubject.createDefault(false)
val messageSendingObservable: Observable = _messageSendingObservable
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
index d6324446e..c26c8cdc0 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
@@ -5,7 +5,7 @@ import com.glia.androidsdk.queuing.Queue
import com.glia.androidsdk.queuing.QueueState
import com.glia.widgets.core.queue.GliaQueueRepository
import com.glia.widgets.helper.rx.Schedulers
-import com.glia.widgets.helper.supportsMessaging
+import com.glia.widgets.helper.supportMessaging
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
@@ -36,5 +36,5 @@ internal class IsMessagingAvailableUseCase(
.filter { queueIds.contains(it.id) }
.filterNot { it.state.status == QueueState.Status.CLOSED }
.filterNot { it.state.status == QueueState.Status.UNKNOWN }
- .any { it.supportsMessaging() }
+ .any { it.supportMessaging() }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
index 901b2df52..d2486a86b 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
@@ -7,7 +7,7 @@ import com.glia.widgets.helper.rx.Schedulers
import com.glia.widgets.messagecenter.MessageCenterState
import io.reactivex.Observable
-class SendMessageButtonStateUseCase(
+internal class SendMessageButtonStateUseCase(
private val sendMessageRepository: SendMessageRepository,
private val fileAttachmentRepository: SecureFileAttachmentRepository,
private val secureConversationsRepository: SecureConversationsRepository,
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
index 1d66dba94..b4e8477d7 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
@@ -10,7 +10,7 @@ import com.glia.widgets.core.fileupload.model.FileAttachment
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
import com.glia.widgets.core.secureconversations.SendMessageRepository
-class SendSecureMessageUseCase(
+internal class SendSecureMessageUseCase(
private val queueId: String,
private val sendMessageRepository: SendMessageRepository,
private val secureConversationsRepository: SecureConversationsRepository,
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java b/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
index 2f40bf5fa..fc483182a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
@@ -47,6 +47,7 @@ public class ControllerFactory {
private final GliaSdkConfigurationManager sdkConfigurationManager;
private final FilePreviewController filePreviewController;
private final ChatHeadPosition chatHeadPosition;
+ private final ManagerFactory managerFactory;
private ChatController retainedChatController;
private CallController retainedCallController;
private ScreenSharingController retainedScreenSharingController;
@@ -56,87 +57,84 @@ public class ControllerFactory {
private ActivityWatcherForChatHeadController activityWatcherForChatHeadController;
public ControllerFactory(
- RepositoryFactory repositoryFactory,
- UseCaseFactory useCaseFactory,
- GliaSdkConfigurationManager sdkConfigurationManager
+ RepositoryFactory repositoryFactory,
+ UseCaseFactory useCaseFactory,
+ GliaSdkConfigurationManager sdkConfigurationManager,
+ ManagerFactory managerFactory
) {
this.repositoryFactory = repositoryFactory;
messagesNotSeenHandler = new MessagesNotSeenHandler(
- useCaseFactory.createGliaOnMessageUseCase(),
- useCaseFactory.createOnEngagementEndUseCase()
+ useCaseFactory.createGliaOnMessageUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase()
);
this.useCaseFactory = useCaseFactory;
this.dialogController = new DialogController(
- useCaseFactory.createSetOverlayPermissionRequestDialogShownUseCase(),
- useCaseFactory.createSetEnableCallNotificationChannelDialogShownUseCase()
+ useCaseFactory.createSetOverlayPermissionRequestDialogShownUseCase(),
+ useCaseFactory.createSetEnableCallNotificationChannelDialogShownUseCase()
);
this.filePreviewController = new FilePreviewController(
- useCaseFactory.createGetImageFileFromDownloadsUseCase(),
- useCaseFactory.createGetImageFileFromCacheUseCase(),
- useCaseFactory.createPutImageFileToDownloadsUseCase(),
- useCaseFactory.createOnEngagementEndUseCase()
+ useCaseFactory.createGetImageFileFromDownloadsUseCase(),
+ useCaseFactory.createGetImageFileFromCacheUseCase(),
+ useCaseFactory.createPutImageFileToDownloadsUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase()
);
this.chatHeadPosition = ChatHeadPosition.getInstance();
this.sdkConfigurationManager = sdkConfigurationManager;
+ this.managerFactory = managerFactory;
}
public ChatController getChatController(ChatViewCallback chatViewCallback) {
if (retainedChatController == null) {
Logger.d(TAG, "new for chat activity");
retainedChatController = new ChatController(
- chatViewCallback,
- repositoryFactory.getMediaUpgradeOfferRepository(),
- sharedTimer,
- minimizeHandler,
- dialogController,
- messagesNotSeenHandler,
- useCaseFactory.createCallNotificationUseCase(),
- useCaseFactory.createGliaLoadHistoryUseCase(),
- useCaseFactory.createQueueForChatEngagementUseCase(),
- useCaseFactory.createOnEngagementUseCase(),
- useCaseFactory.createOnEngagementEndUseCase(),
- useCaseFactory.createGliaOnMessageUseCase(),
- useCaseFactory.createGliaOnOperatorTypingUseCase(),
- useCaseFactory.createGliaSendMessagePreviewUseCase(),
- useCaseFactory.createGliaSendMessageUseCase(),
- useCaseFactory.createAddOperatorMediaStateListenerUseCase(),
- useCaseFactory.createCancelQueueTicketUseCase(),
- useCaseFactory.createEndEngagementUseCase(),
- useCaseFactory.createAddFileToAttachmentAndUploadUseCase(),
- useCaseFactory.createAddFileAttachmentsObserverUseCase(),
- useCaseFactory.createRemoveFileAttachmentObserverUseCase(),
- useCaseFactory.createGetFileAttachmentsUseCase(),
- useCaseFactory.createRemoveFileAttachmentUseCase(),
- useCaseFactory.createSupportedFileCountCheckUseCase(),
- useCaseFactory.createIsShowSendButtonUseCase(),
- useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase(),
- useCaseFactory.createDownloadFileUseCase(),
- useCaseFactory.createIsEnableChatEditTextUseCase(),
- useCaseFactory.createSiteInfoUseCase(),
- useCaseFactory.getGliaSurveyUseCase(),
- useCaseFactory.createGetGliaEngagementStateFlowableUseCase(),
- useCaseFactory.createIsFromCallScreenUseCase(),
- useCaseFactory.createUpdateFromCallScreenUseCase(),
- useCaseFactory.createCustomCardAdapterTypeUseCase(),
- useCaseFactory.createCustomCardTypeUseCase(),
- useCaseFactory.createCustomCardShouldShowUseCase(),
- useCaseFactory.createQueueTicketStateChangeToUnstaffedUseCase(),
- useCaseFactory.createIsQueueingEngagementUseCase(),
- useCaseFactory.createAddMediaUpgradeOfferCallbackUseCase(),
- useCaseFactory.createRemoveMediaUpgradeOfferCallbackUseCase(),
- useCaseFactory.createIsSecureEngagementUseCase(),
- useCaseFactory.createIsOngoingEngagementUseCase(),
- useCaseFactory.createSetEngagementConfigUseCase(),
- useCaseFactory.createIsSecureConversationsChatAvailableUseCase(),
- useCaseFactory.createMarkMessagesReadUseCase(),
- useCaseFactory.createHasPendingSurveyUseCase(),
- useCaseFactory.createSetPendingSurveyUsed(),
- useCaseFactory.createIsCallVisualizerUseCase(),
- useCaseFactory.createPreEngagementMessageUseCase(),
- useCaseFactory.createAddNewMessagesDividerUseCase(),
- useCaseFactory.createIsFileReadyForPreviewUseCase(),
- useCaseFactory.createAcceptMediaUpgradeOfferUseCase()
+ chatViewCallback,
+ repositoryFactory.getMediaUpgradeOfferRepository(),
+ sharedTimer,
+ minimizeHandler,
+ dialogController,
+ messagesNotSeenHandler,
+ useCaseFactory.createCallNotificationUseCase(),
+ useCaseFactory.createQueueForChatEngagementUseCase(),
+ useCaseFactory.createOnEngagementUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase(),
+ useCaseFactory.createGliaOnOperatorTypingUseCase(),
+ useCaseFactory.createGliaSendMessagePreviewUseCase(),
+ useCaseFactory.createGliaSendMessageUseCase(),
+ useCaseFactory.createAddOperatorMediaStateListenerUseCase(),
+ useCaseFactory.createCancelQueueTicketUseCase(),
+ useCaseFactory.createEndEngagementUseCase(),
+ useCaseFactory.createAddFileToAttachmentAndUploadUseCase(),
+ useCaseFactory.createAddFileAttachmentsObserverUseCase(),
+ useCaseFactory.createRemoveFileAttachmentObserverUseCase(),
+ useCaseFactory.createGetFileAttachmentsUseCase(),
+ useCaseFactory.createRemoveFileAttachmentUseCase(),
+ useCaseFactory.createSupportedFileCountCheckUseCase(),
+ useCaseFactory.createIsShowSendButtonUseCase(),
+ useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase(),
+ useCaseFactory.createDownloadFileUseCase(),
+ useCaseFactory.createSiteInfoUseCase(),
+ useCaseFactory.getGliaSurveyUseCase(),
+ useCaseFactory.createGetGliaEngagementStateFlowableUseCase(),
+ useCaseFactory.createIsFromCallScreenUseCase(),
+ useCaseFactory.createUpdateFromCallScreenUseCase(),
+ useCaseFactory.createQueueTicketStateChangeToUnstaffedUseCase(),
+ useCaseFactory.createIsQueueingEngagementUseCase(),
+ useCaseFactory.createAddMediaUpgradeOfferCallbackUseCase(),
+ useCaseFactory.createRemoveMediaUpgradeOfferCallbackUseCase(),
+ useCaseFactory.createIsSecureEngagementUseCase(),
+ useCaseFactory.createIsOngoingEngagementUseCase(),
+ useCaseFactory.createSetEngagementConfigUseCase(),
+ useCaseFactory.createIsSecureConversationsChatAvailableUseCase(),
+ useCaseFactory.createHasPendingSurveyUseCase(),
+ useCaseFactory.createSetPendingSurveyUsed(),
+ useCaseFactory.createIsCallVisualizerUseCase(),
+ useCaseFactory.createIsFileReadyForPreviewUseCase(),
+ useCaseFactory.createAcceptMediaUpgradeOfferUseCase(),
+ useCaseFactory.createDetermineGvaButtonTypeUseCase(),
+ useCaseFactory.createIsAuthenticatedUseCase(),
+ useCaseFactory.createUpdateOperatorDefaultImageUrlUseCase(),
+ managerFactory.getChatManager()
);
} else {
Logger.d(TAG, "retained chat controller");
@@ -149,42 +147,42 @@ public CallController getCallController(CallViewCallback callViewCallback) {
if (retainedCallController == null) {
Logger.d(TAG, "new call controller");
retainedCallController = new CallController(
- sdkConfigurationManager,
- repositoryFactory.getMediaUpgradeOfferRepository(),
- sharedTimer,
- callViewCallback,
- new TimeCounter(),
- new TimeCounter(),
- minimizeHandler,
- dialogController,
- messagesNotSeenHandler,
- useCaseFactory.createCallNotificationUseCase(),
- useCaseFactory.createQueueForMediaEngagementUseCase(),
- useCaseFactory.createCancelQueueTicketUseCase(),
- useCaseFactory.createOnEngagementUseCase(),
- useCaseFactory.createAddOperatorMediaStateListenerUseCase(),
- useCaseFactory.createRemoveOperatorMediaStateListenerUseCase(),
- useCaseFactory.createOnEngagementEndUseCase(),
- useCaseFactory.createEndEngagementUseCase(),
- useCaseFactory.createShouldShowMediaEngagementViewUseCase(),
- useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase(),
- useCaseFactory.createHasCallNotificationChannelEnabledUseCase(),
- useCaseFactory.createIsShowEnableCallNotificationChannelDialogUseCase(),
- useCaseFactory.getGliaSurveyUseCase(),
- useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
- useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
- useCaseFactory.createAddMediaUpgradeOfferCallbackUseCase(),
- useCaseFactory.createRemoveMediaUpgradeOfferCallbackUseCase(),
- useCaseFactory.createToggleVisitorAudioMediaMuteUseCase(),
- useCaseFactory.createToggleVisitorVideoUseCase(),
- useCaseFactory.createGetGliaEngagementStateFlowableUseCase(),
- useCaseFactory.createUpdateFromCallScreenUseCase(),
- useCaseFactory.createQueueTicketStateChangeToUnstaffedUseCase(),
- useCaseFactory.createIsCallVisualizerUseCase(),
- useCaseFactory.createIsOngoingEngagementUseCase(),
- useCaseFactory.createSetPendingSurveyUsed(),
- useCaseFactory.createTurnSpeakerphoneUseCase(),
- useCaseFactory.createHandleCallPermissionsUseCase());
+ sdkConfigurationManager,
+ repositoryFactory.getMediaUpgradeOfferRepository(),
+ sharedTimer,
+ callViewCallback,
+ new TimeCounter(),
+ new TimeCounter(),
+ minimizeHandler,
+ dialogController,
+ messagesNotSeenHandler,
+ useCaseFactory.createCallNotificationUseCase(),
+ useCaseFactory.createQueueForMediaEngagementUseCase(),
+ useCaseFactory.createCancelQueueTicketUseCase(),
+ useCaseFactory.createOnEngagementUseCase(),
+ useCaseFactory.createAddOperatorMediaStateListenerUseCase(),
+ useCaseFactory.createRemoveOperatorMediaStateListenerUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase(),
+ useCaseFactory.createEndEngagementUseCase(),
+ useCaseFactory.createShouldShowMediaEngagementViewUseCase(),
+ useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase(),
+ useCaseFactory.createHasCallNotificationChannelEnabledUseCase(),
+ useCaseFactory.createIsShowEnableCallNotificationChannelDialogUseCase(),
+ useCaseFactory.getGliaSurveyUseCase(),
+ useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createAddMediaUpgradeOfferCallbackUseCase(),
+ useCaseFactory.createRemoveMediaUpgradeOfferCallbackUseCase(),
+ useCaseFactory.createToggleVisitorAudioMediaMuteUseCase(),
+ useCaseFactory.createToggleVisitorVideoUseCase(),
+ useCaseFactory.createGetGliaEngagementStateFlowableUseCase(),
+ useCaseFactory.createUpdateFromCallScreenUseCase(),
+ useCaseFactory.createQueueTicketStateChangeToUnstaffedUseCase(),
+ useCaseFactory.createIsCallVisualizerUseCase(),
+ useCaseFactory.createIsOngoingEngagementUseCase(),
+ useCaseFactory.createSetPendingSurveyUsed(),
+ useCaseFactory.createTurnSpeakerphoneUseCase(),
+ useCaseFactory.createHandleCallPermissionsUseCase());
} else {
Logger.d(TAG, "retained call controller");
retainedCallController.setViewCallback(callViewCallback);
@@ -196,12 +194,12 @@ public ScreenSharingController getScreenSharingController() {
if (retainedScreenSharingController == null) {
Logger.d(TAG, "new screen sharing controller");
retainedScreenSharingController = new ScreenSharingController(
- repositoryFactory.getGliaScreenSharingRepository(),
- dialogController,
- useCaseFactory.createShowScreenSharingNotificationUseCase(),
- useCaseFactory.createRemoveScreenSharingNotificationUseCase(),
- useCaseFactory.createHasScreenSharingNotificationChannelEnabledUseCase(),
- sdkConfigurationManager
+ repositoryFactory.getGliaScreenSharingRepository(),
+ dialogController,
+ useCaseFactory.createShowScreenSharingNotificationUseCase(),
+ useCaseFactory.createRemoveScreenSharingNotificationUseCase(),
+ useCaseFactory.createHasScreenSharingNotificationChannelEnabledUseCase(),
+ sdkConfigurationManager
);
}
return retainedScreenSharingController;
@@ -237,8 +235,8 @@ public DialogController getDialogController() {
public DialogController createDialogController() {
return new DialogController(
- useCaseFactory.createSetOverlayPermissionRequestDialogShownUseCase(),
- useCaseFactory.createSetEnableCallNotificationChannelDialogShownUseCase()
+ useCaseFactory.createSetOverlayPermissionRequestDialogShownUseCase(),
+ useCaseFactory.createSetEnableCallNotificationChannelDialogShownUseCase()
);
}
@@ -256,19 +254,19 @@ public FilePreviewController getImagePreviewController() {
public ServiceChatHeadController getChatHeadController() {
if (serviceChatHeadController == null) {
serviceChatHeadController = new ServiceChatHeadController(
- useCaseFactory.getToggleChatHeadServiceUseCase(),
- useCaseFactory.getResolveChatHeadNavigationUseCase(),
- useCaseFactory.createOnEngagementUseCase(),
- useCaseFactory.createOnCallVisualizerUseCase(),
- useCaseFactory.createOnEngagementEndUseCase(),
- useCaseFactory.createOnCallVisualizerEndUseCase(),
- messagesNotSeenHandler,
- useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
- useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
- chatHeadPosition,
- useCaseFactory.createGetOperatorFlowableUseCase(),
- useCaseFactory.createSetPendingSurveyUseCase(),
- useCaseFactory.createIsCallVisualizerScreenSharingUseCase()
+ useCaseFactory.getToggleChatHeadServiceUseCase(),
+ useCaseFactory.getResolveChatHeadNavigationUseCase(),
+ useCaseFactory.createOnEngagementUseCase(),
+ useCaseFactory.createOnCallVisualizerUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase(),
+ useCaseFactory.createOnCallVisualizerEndUseCase(),
+ messagesNotSeenHandler,
+ useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
+ chatHeadPosition,
+ useCaseFactory.createGetOperatorFlowableUseCase(),
+ useCaseFactory.createSetPendingSurveyUseCase(),
+ useCaseFactory.createIsCallVisualizerScreenSharingUseCase()
);
}
return serviceChatHeadController;
@@ -277,18 +275,18 @@ public ServiceChatHeadController getChatHeadController() {
public ApplicationChatHeadLayoutController getChatHeadLayoutController() {
if (applicationChatHeadController == null) {
applicationChatHeadController = new ApplicationChatHeadLayoutController(
- useCaseFactory.getIsDisplayApplicationChatHeadUseCase(),
- useCaseFactory.getResolveChatHeadNavigationUseCase(),
- useCaseFactory.createOnEngagementUseCase(),
- useCaseFactory.createOnEngagementEndUseCase(),
- useCaseFactory.createOnCallVisualizerUseCase(),
- useCaseFactory.createOnCallVisualizerEndUseCase(),
- messagesNotSeenHandler,
- useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
- useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
- useCaseFactory.createGetOperatorFlowableUseCase(),
- useCaseFactory.createSetPendingSurveyUseCase(),
- useCaseFactory.createIsCallVisualizerScreenSharingUseCase()
+ useCaseFactory.getIsDisplayApplicationChatHeadUseCase(),
+ useCaseFactory.getResolveChatHeadNavigationUseCase(),
+ useCaseFactory.createOnEngagementUseCase(),
+ useCaseFactory.createOnEngagementEndUseCase(),
+ useCaseFactory.createOnCallVisualizerUseCase(),
+ useCaseFactory.createOnCallVisualizerEndUseCase(),
+ messagesNotSeenHandler,
+ useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createGetOperatorFlowableUseCase(),
+ useCaseFactory.createSetPendingSurveyUseCase(),
+ useCaseFactory.createIsCallVisualizerScreenSharingUseCase()
);
}
return applicationChatHeadController;
@@ -297,7 +295,7 @@ public ApplicationChatHeadLayoutController getChatHeadLayoutController() {
public SurveyContract.Controller getSurveyController() {
if (surveyController == null) {
surveyController = new SurveyController(
- useCaseFactory.getSurveyAnswerUseCase()
+ useCaseFactory.getSurveyAnswerUseCase()
);
}
return surveyController;
@@ -306,12 +304,12 @@ public SurveyContract.Controller getSurveyController() {
public CallVisualizerController getCallVisualizerController() {
if (callVisualizerController == null) {
callVisualizerController = new CallVisualizerController(
- repositoryFactory.getCallVisualizerRepository(),
- dialogController,
- useCaseFactory.getGliaSurveyUseCase(),
- useCaseFactory.createOnCallVisualizerUseCase(),
- useCaseFactory.createOnCallVisualizerEndUseCase(),
- useCaseFactory.createIsCallOrChatScreenActiveUseCase()
+ repositoryFactory.getCallVisualizerRepository(),
+ dialogController,
+ useCaseFactory.getGliaSurveyUseCase(),
+ useCaseFactory.createOnCallVisualizerUseCase(),
+ useCaseFactory.createOnCallVisualizerEndUseCase(),
+ useCaseFactory.createIsCallOrChatScreenActiveUseCase()
);
}
return callVisualizerController;
@@ -319,29 +317,29 @@ public CallVisualizerController getCallVisualizerController() {
public FloatingVisitorVideoContract.Controller getFloatingVisitorVideoController() {
return new FloatingVisitorVideoController(
- useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
- useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
- useCaseFactory.createIsShowVideoUseCase(),
- useCaseFactory.createIsShowOnHoldUseCase()
+ useCaseFactory.createAddVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createRemoveVisitorMediaStateListenerUseCase(),
+ useCaseFactory.createIsShowVideoUseCase(),
+ useCaseFactory.createIsShowOnHoldUseCase()
);
}
public MessageCenterContract.Controller getMessageCenterController(String queueId) {
return new MessageCenterController(
- serviceChatHeadController,
- useCaseFactory.createSendSecureMessageUseCase(queueId),
- useCaseFactory.createIsMessageCenterAvailableUseCase(queueId),
- useCaseFactory.createAddSecureFileAttachmentsObserverUseCase(),
- useCaseFactory.createAddSecureFileToAttachmentAndUploadUseCase(),
- useCaseFactory.createGetSecureFileAttachmentsUseCase(),
- useCaseFactory.createRemoveSecureFileAttachmentUseCase(),
- useCaseFactory.createIsAuthenticatedUseCase(),
- useCaseFactory.createSiteInfoUseCase(),
- useCaseFactory.createOnNextMessageUseCase(),
- useCaseFactory.createEnableSendMessageButtonUseCase(),
- useCaseFactory.createShowMessageLimitErrorUseCase(),
- useCaseFactory.createResetMessageCenterUseCase(),
- createDialogController()
+ serviceChatHeadController,
+ useCaseFactory.createSendSecureMessageUseCase(queueId),
+ useCaseFactory.createIsMessageCenterAvailableUseCase(queueId),
+ useCaseFactory.createAddSecureFileAttachmentsObserverUseCase(),
+ useCaseFactory.createAddSecureFileToAttachmentAndUploadUseCase(),
+ useCaseFactory.createGetSecureFileAttachmentsUseCase(),
+ useCaseFactory.createRemoveSecureFileAttachmentUseCase(),
+ useCaseFactory.createIsAuthenticatedUseCase(),
+ useCaseFactory.createSiteInfoUseCase(),
+ useCaseFactory.createOnNextMessageUseCase(),
+ useCaseFactory.createEnableSendMessageButtonUseCase(),
+ useCaseFactory.createShowMessageLimitErrorUseCase(),
+ useCaseFactory.createResetMessageCenterUseCase(),
+ createDialogController()
);
}
@@ -351,17 +349,17 @@ public EndScreenSharingContract.Controller getEndScreenSharingController() {
public VisitorCodeContract.Controller getVisitorCodeController() {
return new VisitorCodeController(
- dialogController,
- repositoryFactory.getVisitorCodeRepository(),
- repositoryFactory.getGliaEngagementRepository());
+ dialogController,
+ repositoryFactory.getVisitorCodeRepository(),
+ repositoryFactory.getGliaEngagementRepository());
}
public ActivityWatcherForCallVisualizerContract.Controller getActivityWatcherForCallVisualizerController() {
if (activityWatcherforCallVisualizerController == null) {
activityWatcherforCallVisualizerController = new ActivityWatcherForCallVisualizerController(
- getCallVisualizerController(),
- getScreenSharingController(),
- useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase());
+ getCallVisualizerController(),
+ getScreenSharingController(),
+ useCaseFactory.createIsShowOverlayPermissionRequestDialogUseCase());
}
return activityWatcherforCallVisualizerController;
}
@@ -369,19 +367,19 @@ public ActivityWatcherForCallVisualizerContract.Controller getActivityWatcherFor
public ActivityWatcherForChatHeadContract.Controller getActivityWatcherForChatHeadController() {
if (activityWatcherForChatHeadController == null) {
activityWatcherForChatHeadController = new ActivityWatcherForChatHeadController(
- serviceChatHeadController,
- getChatHeadLayoutController(),
- getScreenSharingController(),
- useCaseFactory.createOnEngagementUseCase(),
- useCaseFactory.createIsFromCallScreenUseCase(),
- useCaseFactory.createUpdateFromCallScreenUseCase());
+ serviceChatHeadController,
+ getChatHeadLayoutController(),
+ getScreenSharingController(),
+ useCaseFactory.createOnEngagementUseCase(),
+ useCaseFactory.createIsFromCallScreenUseCase(),
+ useCaseFactory.createUpdateFromCallScreenUseCase());
}
return activityWatcherForChatHeadController;
}
public PermissionsRequestContract.Controller getPermissionsController() {
return new PermissionsRequestController(
- repositoryFactory.getPermissionsRequestRepository()
+ repositoryFactory.getPermissionsRequestRepository()
);
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.java b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.java
index dffbdcdd8..ea95f13d2 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.java
@@ -1,7 +1,6 @@
package com.glia.widgets.di;
import android.app.Application;
-import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
@@ -19,40 +18,35 @@
import com.glia.widgets.core.dialog.PermissionDialogManager;
import com.glia.widgets.core.notification.device.INotificationManager;
import com.glia.widgets.core.notification.device.NotificationManager;
-import com.glia.widgets.permissions.ActivityWatcherForPermissionsRequest;
import com.glia.widgets.core.permissions.PermissionManager;
import com.glia.widgets.filepreview.data.source.local.DownloadsFolderDataSource;
import com.glia.widgets.helper.ApplicationLifecycleManager;
import com.glia.widgets.helper.Logger;
import com.glia.widgets.helper.ResourceProvider;
import com.glia.widgets.helper.rx.GliaWidgetsSchedulers;
+import com.glia.widgets.permissions.ActivityWatcherForPermissionsRequest;
import com.glia.widgets.view.head.ActivityWatcherForChatHead;
import com.glia.widgets.view.head.controller.ServiceChatHeadController;
import com.glia.widgets.view.unifiedui.theme.UnifiedThemeManager;
-import kotlin.jvm.functions.Function2;
-
public class Dependencies {
private final static String TAG = "Dependencies";
-
+ private static final UnifiedThemeManager UNIFIED_THEME_MANAGER = new UnifiedThemeManager();
private static ControllerFactory controllerFactory;
private static INotificationManager notificationManager;
private static CallVisualizerManager callVisualizerManager;
private static GliaSdkConfigurationManager sdkConfigurationManager =
new GliaSdkConfigurationManager();
private static UseCaseFactory useCaseFactory;
+ private static ManagerFactory managerFactory;
private static GliaCore gliaCore = new GliaCoreImpl();
private static ResourceProvider resourceProvider;
- private static final UnifiedThemeManager UNIFIED_THEME_MANAGER = new UnifiedThemeManager();
public static void onAppCreate(Application application) {
notificationManager = new NotificationManager(application);
DownloadsFolderDataSource downloadsFolderDataSource = new DownloadsFolderDataSource(application);
- RepositoryFactory repositoryFactory = new RepositoryFactory(
- gliaCore,
- downloadsFolderDataSource
- );
+ RepositoryFactory repositoryFactory = new RepositoryFactory(gliaCore, downloadsFolderDataSource);
PermissionManager permissionManager = new PermissionManager(
application,
@@ -72,20 +66,12 @@ public static void onAppCreate(Application application) {
new GliaWidgetsSchedulers(),
gliaCore
);
- initAudioControlManager(
- audioControlManager,
- useCaseFactory.createOnAudioStartedUseCase()
- );
+ initAudioControlManager(audioControlManager, useCaseFactory.createOnAudioStartedUseCase());
- controllerFactory = new ControllerFactory(
- repositoryFactory,
- useCaseFactory,
- sdkConfigurationManager
- );
- initApplicationLifecycleObserver(
- new ApplicationLifecycleManager(),
- controllerFactory.getChatHeadController()
- );
+ managerFactory = new ManagerFactory(useCaseFactory);
+
+ controllerFactory = new ControllerFactory(repositoryFactory, useCaseFactory, sdkConfigurationManager, managerFactory);
+ initApplicationLifecycleObserver(new ApplicationLifecycleManager(), controllerFactory.getChatHeadController());
ActivityWatcherForCallVisualizer activityWatcherForCallVisualizer =
new ActivityWatcherForCallVisualizer(
@@ -96,8 +82,7 @@ public static void onAppCreate(Application application) {
ActivityWatcherForChatHead activityWatcherForChatHead =
new ActivityWatcherForChatHead(
- getControllerFactory().getActivityWatcherForChatHeadController()
- );
+ getControllerFactory().getActivityWatcherForChatHeadController());
application.registerActivityLifecycleCallbacks(activityWatcherForChatHead);
ActivityWatcherForPermissionsRequest activityWatcherForPermissionsRequest =
@@ -123,6 +108,11 @@ public static GliaSdkConfigurationManager getSdkConfigurationManager() {
return sdkConfigurationManager;
}
+ @VisibleForTesting
+ public static void setSdkConfigurationManager(@NonNull GliaSdkConfigurationManager sdkConfigurationManager) {
+ Dependencies.sdkConfigurationManager = sdkConfigurationManager;
+ }
+
@NonNull
public static UnifiedThemeManager getGliaThemeManager() {
return UNIFIED_THEME_MANAGER;
@@ -141,6 +131,11 @@ public static ControllerFactory getControllerFactory() {
return controllerFactory;
}
+ @VisibleForTesting
+ public static void setControllerFactory(ControllerFactory controllerFactory) {
+ Dependencies.controllerFactory = controllerFactory;
+ }
+
public static GliaCore glia() {
return gliaCore;
}
@@ -158,16 +153,6 @@ public static ResourceProvider getResourceProvider() {
return resourceProvider;
}
- @VisibleForTesting
- public static void setControllerFactory(ControllerFactory controllerFactory) {
- Dependencies.controllerFactory = controllerFactory;
- }
-
- @VisibleForTesting
- public static void setSdkConfigurationManager(@NonNull GliaSdkConfigurationManager sdkConfigurationManager) {
- Dependencies.sdkConfigurationManager = sdkConfigurationManager;
- }
-
@VisibleForTesting
public static void setResourceProvider(ResourceProvider resourceProvider) {
Dependencies.resourceProvider = resourceProvider;
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt b/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt
new file mode 100644
index 000000000..808d8e161
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt
@@ -0,0 +1,20 @@
+package com.glia.widgets.di
+
+import com.glia.widgets.chat.ChatManager
+
+internal class ManagerFactory(private val useCaseFactory: UseCaseFactory) {
+ val chatManager: ChatManager
+ get() = useCaseFactory.run {
+ ChatManager(
+ onMessageUseCase = createGliaOnMessageUseCase(),
+ loadHistoryUseCase = createGliaLoadHistoryUseCase(),
+ addNewMessagesDividerUseCase = createAddNewMessagesDividerUseCase(),
+ markMessagesReadWithDelayUseCase = createMarkMessagesReadUseCase(),
+ appendHistoryChatMessageUseCase = createAppendHistoryChatMessageUseCase(),
+ appendNewChatMessageUseCase = createAppendNewChatMessageUseCase(),
+ sendUnsentMessagesUseCase = createSendUnsentMessagesUseCase(),
+ handleCustomCardClickUseCase = createHandleCustomCardClickUseCase(),
+ isAuthenticatedUseCase = createIsAuthenticatedUseCase()
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java b/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
index 43ea3c552..431f58719 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
@@ -9,6 +9,17 @@
import com.glia.widgets.call.domain.ToggleVisitorVideoUseCase;
import com.glia.widgets.callvisualizer.domain.IsCallOrChatScreenActiveUseCase;
import com.glia.widgets.chat.domain.AddNewMessagesDividerUseCase;
+import com.glia.widgets.chat.domain.AppendGvaMessageItemUseCase;
+import com.glia.widgets.chat.domain.AppendHistoryChatMessageUseCase;
+import com.glia.widgets.chat.domain.AppendHistoryCustomCardItemUseCase;
+import com.glia.widgets.chat.domain.AppendHistoryOperatorChatItemUseCase;
+import com.glia.widgets.chat.domain.AppendHistoryResponseCardOrTextItemUseCase;
+import com.glia.widgets.chat.domain.AppendHistoryVisitorChatItemUseCase;
+import com.glia.widgets.chat.domain.AppendNewChatMessageUseCase;
+import com.glia.widgets.chat.domain.AppendNewOperatorMessageUseCase;
+import com.glia.widgets.chat.domain.AppendNewResponseCardOrTextItemUseCase;
+import com.glia.widgets.chat.domain.AppendNewVisitorMessageUseCase;
+import com.glia.widgets.chat.domain.AppendSystemMessageItemUseCase;
import com.glia.widgets.chat.domain.CustomCardAdapterTypeUseCase;
import com.glia.widgets.chat.domain.CustomCardShouldShowUseCase;
import com.glia.widgets.chat.domain.CustomCardTypeUseCase;
@@ -19,14 +30,29 @@
import com.glia.widgets.chat.domain.GliaOnOperatorTypingUseCase;
import com.glia.widgets.chat.domain.GliaSendMessagePreviewUseCase;
import com.glia.widgets.chat.domain.GliaSendMessageUseCase;
+import com.glia.widgets.chat.domain.HandleCustomCardClickUseCase;
import com.glia.widgets.chat.domain.IsAuthenticatedUseCase;
-import com.glia.widgets.chat.domain.IsEnableChatEditTextUseCase;
import com.glia.widgets.chat.domain.IsFromCallScreenUseCase;
import com.glia.widgets.chat.domain.IsSecureConversationsChatAvailableUseCase;
import com.glia.widgets.chat.domain.IsShowSendButtonUseCase;
-import com.glia.widgets.chat.domain.PreEngagementMessageUseCase;
+import com.glia.widgets.chat.domain.MapOperatorAttachmentUseCase;
+import com.glia.widgets.chat.domain.MapOperatorPlainTextUseCase;
+import com.glia.widgets.chat.domain.MapResponseCardUseCase;
+import com.glia.widgets.chat.domain.MapVisitorAttachmentUseCase;
+import com.glia.widgets.chat.domain.SendUnsentMessagesUseCase;
import com.glia.widgets.chat.domain.SiteInfoUseCase;
import com.glia.widgets.chat.domain.UpdateFromCallScreenUseCase;
+import com.glia.widgets.chat.domain.gva.DetermineGvaButtonTypeUseCase;
+import com.glia.widgets.chat.domain.gva.DetermineGvaUrlTypeUseCase;
+import com.glia.widgets.chat.domain.gva.GetGvaTypeUseCase;
+import com.glia.widgets.chat.domain.gva.IsGvaUseCase;
+import com.glia.widgets.chat.domain.gva.MapGvaGvaGalleryCardsUseCase;
+import com.glia.widgets.chat.domain.gva.MapGvaPersistentButtonsUseCase;
+import com.glia.widgets.chat.domain.gva.MapGvaQuickRepliesUseCase;
+import com.glia.widgets.chat.domain.gva.MapGvaResponseTextUseCase;
+import com.glia.widgets.chat.domain.gva.MapGvaUseCase;
+import com.glia.widgets.chat.domain.gva.ParseGvaButtonsUseCase;
+import com.glia.widgets.chat.domain.gva.ParseGvaGalleryCardsUseCase;
import com.glia.widgets.core.audio.AudioControlManager;
import com.glia.widgets.core.audio.domain.OnAudioStartedUseCase;
import com.glia.widgets.core.audio.domain.TurnSpeakerphoneUseCase;
@@ -61,6 +87,7 @@
import com.glia.widgets.core.engagement.domain.ResetSurveyUseCase;
import com.glia.widgets.core.engagement.domain.SetEngagementConfigUseCase;
import com.glia.widgets.core.engagement.domain.ShouldShowMediaEngagementViewUseCase;
+import com.glia.widgets.core.engagement.domain.UpdateOperatorDefaultImageUrlUseCase;
import com.glia.widgets.core.fileupload.domain.AddFileAttachmentsObserverUseCase;
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase;
import com.glia.widgets.core.fileupload.domain.GetFileAttachmentsUseCase;
@@ -110,8 +137,11 @@
import com.glia.widgets.helper.rx.Schedulers;
import com.glia.widgets.view.floatingvisitorvideoview.domain.IsShowOnHoldUseCase;
import com.glia.widgets.view.floatingvisitorvideoview.domain.IsShowVideoUseCase;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
public class UseCaseFactory {
+ private static final SurveyStateManager surveyStateManager = new SurveyStateManager();
private static CallNotificationUseCase callNotificationUseCase;
private static ShowScreenSharingNotificationUseCase showScreenSharingNotificationUseCase;
private static RemoveScreenSharingNotificationUseCase removeScreenSharingNotificationUseCase;
@@ -119,20 +149,20 @@ public class UseCaseFactory {
private static IsDisplayApplicationChatHeadUseCase isDisplayApplicationChatHeadUseCase;
private static ResolveChatHeadNavigationUseCase resolveChatHeadNavigationUseCase;
private static VisitorCodeViewBuilderUseCase visitorCodeViewBuilderUseCase;
-
private static GliaQueueForChatEngagementUseCase gliaQueueForChatEngagementUseCase;
private static GliaQueueForMediaEngagementUseCase gliaQueueForMediaEngagementUseCase;
private final RepositoryFactory repositoryFactory;
private final PermissionManager permissionManager;
private final PermissionDialogManager permissionDialogManager;
private final GliaSdkConfigurationManager gliaSdkConfigurationManager;
- private static final SurveyStateManager surveyStateManager = new SurveyStateManager();
private final INotificationManager notificationManager;
private final ChatHeadManager chatHeadManager;
private final AudioControlManager audioControlManager;
private final Schedulers schedulers;
private final GliaCore gliaCore;
+ private Gson gvaGson;
+
public UseCaseFactory(RepositoryFactory repositoryFactory,
PermissionManager permissionManager,
PermissionDialogManager permissionDialogManager,
@@ -153,17 +183,41 @@ public UseCaseFactory(RepositoryFactory repositoryFactory,
this.gliaCore = gliaCore;
}
+ @NonNull
+ private AppendHistoryResponseCardOrTextItemUseCase createAppendHistoryResponseCardOrTextItemUseCase() {
+ return new AppendHistoryResponseCardOrTextItemUseCase(
+ createMapOperatorAttachmentUseCase(),
+ createMapOperatorPlainTextUseCase(),
+ createMapResponseCardUseCase()
+ );
+ }
+
+ @NonNull
+ public MapResponseCardUseCase createMapResponseCardUseCase() {
+ return new MapResponseCardUseCase();
+ }
+
+ @NonNull
+ public MapOperatorAttachmentUseCase createMapOperatorAttachmentUseCase() {
+ return new MapOperatorAttachmentUseCase();
+ }
+
+ @NonNull
+ public MapOperatorPlainTextUseCase createMapOperatorPlainTextUseCase() {
+ return new MapOperatorPlainTextUseCase();
+ }
+
@NonNull
public ToggleChatHeadServiceUseCase getToggleChatHeadServiceUseCase() {
if (toggleChatHeadServiceUseCase == null) {
toggleChatHeadServiceUseCase = new ToggleChatHeadServiceUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaScreenSharingRepository(),
- chatHeadManager,
- permissionManager,
- gliaSdkConfigurationManager,
- repositoryFactory.getGliaEngagementTypeRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaScreenSharingRepository(),
+ chatHeadManager,
+ permissionManager,
+ gliaSdkConfigurationManager,
+ repositoryFactory.getGliaEngagementTypeRepository()
);
}
return toggleChatHeadServiceUseCase;
@@ -173,12 +227,12 @@ public ToggleChatHeadServiceUseCase getToggleChatHeadServiceUseCase() {
public IsDisplayApplicationChatHeadUseCase getIsDisplayApplicationChatHeadUseCase() {
if (isDisplayApplicationChatHeadUseCase == null) {
isDisplayApplicationChatHeadUseCase = new IsDisplayApplicationChatHeadUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaScreenSharingRepository(),
- permissionManager,
- gliaSdkConfigurationManager,
- repositoryFactory.getGliaEngagementTypeRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaScreenSharingRepository(),
+ permissionManager,
+ gliaSdkConfigurationManager,
+ repositoryFactory.getGliaEngagementTypeRepository()
);
}
return isDisplayApplicationChatHeadUseCase;
@@ -188,10 +242,10 @@ public IsDisplayApplicationChatHeadUseCase getIsDisplayApplicationChatHeadUseCas
public ResolveChatHeadNavigationUseCase getResolveChatHeadNavigationUseCase() {
if (resolveChatHeadNavigationUseCase == null) {
resolveChatHeadNavigationUseCase = new ResolveChatHeadNavigationUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaEngagementTypeRepository(),
- createIsCallVisualizerScreenSharingUseCase()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaEngagementTypeRepository(),
+ createIsCallVisualizerScreenSharingUseCase()
);
}
return resolveChatHeadNavigationUseCase;
@@ -229,11 +283,11 @@ public RemoveScreenSharingNotificationUseCase createRemoveScreenSharingNotificat
@NonNull
public GliaLoadHistoryUseCase createGliaLoadHistoryUseCase() {
return new GliaLoadHistoryUseCase(
- repositoryFactory.getGliaMessageRepository(),
- repositoryFactory.getSecureConversationsRepository(),
- createIsSecureEngagementUseCase(),
- getMapOperatorUseCase(),
- createSubscribeToUnreadMessagesCountUseCase()
+ repositoryFactory.getGliaMessageRepository(),
+ repositoryFactory.getSecureConversationsRepository(),
+ createIsSecureEngagementUseCase(),
+ getMapOperatorUseCase(),
+ createSubscribeToUnreadMessagesCountUseCase()
);
}
@@ -246,9 +300,9 @@ public MapOperatorUseCase getMapOperatorUseCase() {
public GliaQueueForChatEngagementUseCase createQueueForChatEngagementUseCase() {
if (gliaQueueForChatEngagementUseCase == null) {
gliaQueueForChatEngagementUseCase = new GliaQueueForChatEngagementUseCase(
- schedulers,
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaEngagementRepository()
+ schedulers,
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaEngagementRepository()
);
}
return gliaQueueForChatEngagementUseCase;
@@ -258,9 +312,9 @@ public GliaQueueForChatEngagementUseCase createQueueForChatEngagementUseCase() {
public GliaQueueForMediaEngagementUseCase createQueueForMediaEngagementUseCase() {
if (gliaQueueForMediaEngagementUseCase == null) {
gliaQueueForMediaEngagementUseCase = new GliaQueueForMediaEngagementUseCase(
- schedulers,
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaEngagementRepository()
+ schedulers,
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaEngagementRepository()
);
}
return gliaQueueForMediaEngagementUseCase;
@@ -269,8 +323,8 @@ public GliaQueueForMediaEngagementUseCase createQueueForMediaEngagementUseCase()
@NonNull
public GliaCancelQueueTicketUseCase createCancelQueueTicketUseCase() {
return new GliaCancelQueueTicketUseCase(
- schedulers,
- repositoryFactory.getGliaQueueRepository()
+ schedulers,
+ repositoryFactory.getGliaQueueRepository()
);
}
@@ -282,26 +336,27 @@ public GliaEndEngagementUseCase createEndEngagementUseCase() {
@NonNull
public GliaOnEngagementUseCase createOnEngagementUseCase() {
return new GliaOnEngagementUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaOperatorMediaRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaVisitorMediaRepository(),
- repositoryFactory.getGliaEngagementStateRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaOperatorMediaRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaVisitorMediaRepository(),
+ repositoryFactory.getGliaEngagementStateRepository(),
+ repositoryFactory.getOperatorRepository()
);
}
@NonNull
public GliaOnEngagementEndUseCase createOnEngagementEndUseCase() {
return new GliaOnEngagementEndUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaOperatorMediaRepository(),
- repositoryFactory.getGliaFileAttachmentRepository(),
- createOnEngagementUseCase(),
- createCallNotificationUseCase(),
- createRemoveScreenSharingNotificationUseCase(),
- repositoryFactory.getGliaSurveyRepository(),
- repositoryFactory.getGliaVisitorMediaRepository(),
- repositoryFactory.getEngagementConfigRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaOperatorMediaRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
+ createOnEngagementUseCase(),
+ createCallNotificationUseCase(),
+ createRemoveScreenSharingNotificationUseCase(),
+ repositoryFactory.getGliaSurveyRepository(),
+ repositoryFactory.getGliaVisitorMediaRepository(),
+ repositoryFactory.getEngagementConfigRepository()
);
}
@@ -320,29 +375,19 @@ public SetPendingSurveyUsedUseCase createSetPendingSurveyUsed() {
return new SetPendingSurveyUsedUseCase(surveyStateManager);
}
- @NonNull
- public PreEngagementMessageUseCase createPreEngagementMessageUseCase() {
- return new PreEngagementMessageUseCase(
- repositoryFactory.getGliaMessageRepository(),
- repositoryFactory.getGliaEngagementRepository(),
- createOnEngagementUseCase(),
- getMapOperatorUseCase()
- );
- }
-
@NonNull
public GliaOnMessageUseCase createGliaOnMessageUseCase() {
return new GliaOnMessageUseCase(
- repositoryFactory.getGliaMessageRepository(),
- createOnEngagementUseCase(),
- getMapOperatorUseCase());
+ repositoryFactory.getGliaMessageRepository(),
+ getMapOperatorUseCase()
+ );
}
@NonNull
public GliaOnOperatorTypingUseCase createGliaOnOperatorTypingUseCase() {
return new GliaOnOperatorTypingUseCase(
- repositoryFactory.getGliaMessageRepository(),
- createOnEngagementUseCase()
+ repositoryFactory.getGliaMessageRepository(),
+ createOnEngagementUseCase()
);
}
@@ -354,35 +399,35 @@ public GliaSendMessagePreviewUseCase createGliaSendMessagePreviewUseCase() {
@NonNull
public GliaSendMessageUseCase createGliaSendMessageUseCase() {
return new GliaSendMessageUseCase(
- repositoryFactory.getGliaMessageRepository(),
- repositoryFactory.getGliaFileAttachmentRepository(),
- repositoryFactory.getGliaEngagementStateRepository(),
- repositoryFactory.getEngagementConfigRepository(),
- repositoryFactory.getSecureConversationsRepository(),
- createIsSecureEngagementUseCase()
+ repositoryFactory.getGliaMessageRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
+ repositoryFactory.getGliaEngagementStateRepository(),
+ repositoryFactory.getEngagementConfigRepository(),
+ repositoryFactory.getSecureConversationsRepository(),
+ createIsSecureEngagementUseCase()
);
}
@NonNull
public AddOperatorMediaStateListenerUseCase createAddOperatorMediaStateListenerUseCase() {
return new AddOperatorMediaStateListenerUseCase(
- repositoryFactory.getGliaOperatorMediaRepository()
+ repositoryFactory.getGliaOperatorMediaRepository()
);
}
@NonNull
public RemoveOperatorMediaStateListenerUseCase createRemoveOperatorMediaStateListenerUseCase() {
return new RemoveOperatorMediaStateListenerUseCase(
- repositoryFactory.getGliaOperatorMediaRepository()
+ repositoryFactory.getGliaOperatorMediaRepository()
);
}
@NonNull
public ShouldShowMediaEngagementViewUseCase createShouldShowMediaEngagementViewUseCase() {
return new ShouldShowMediaEngagementViewUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaEngagementTypeRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaEngagementTypeRepository()
);
}
@@ -394,9 +439,9 @@ public AddFileAttachmentsObserverUseCase createAddFileAttachmentsObserverUseCase
@NonNull
public AddFileToAttachmentAndUploadUseCase createAddFileToAttachmentAndUploadUseCase() {
return new AddFileToAttachmentAndUploadUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaFileAttachmentRepository(),
- repositoryFactory.getEngagementConfigRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
+ repositoryFactory.getEngagementConfigRepository()
);
}
@@ -423,9 +468,9 @@ public SupportedFileCountCheckUseCase createSupportedFileCountCheckUseCase() {
@NonNull
public IsShowSendButtonUseCase createIsShowSendButtonUseCase() {
return new IsShowSendButtonUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaFileAttachmentRepository(),
- createIsSecureEngagementUseCase()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
+ createIsSecureEngagementUseCase()
);
}
@@ -472,8 +517,8 @@ public GetImageFileFromCacheUseCase createGetImageFileFromCacheUseCase() {
@NonNull
public GetImageFileFromNetworkUseCase createGetImageFileFromNetworkUseCase() {
return new GetImageFileFromNetworkUseCase(
- repositoryFactory.getGliaFileRepository(),
- createDecodeSampledBitmapFromInputStreamUseCase()
+ repositoryFactory.getGliaFileRepository(),
+ createDecodeSampledBitmapFromInputStreamUseCase()
);
}
@@ -487,11 +532,6 @@ public DownloadFileUseCase createDownloadFileUseCase() {
return new DownloadFileUseCase(repositoryFactory.getGliaFileRepository());
}
- @NonNull
- public IsEnableChatEditTextUseCase createIsEnableChatEditTextUseCase() {
- return new IsEnableChatEditTextUseCase();
- }
-
@NonNull
public OnNextMessageUseCase createOnNextMessageUseCase() {
return new OnNextMessageUseCase(repositoryFactory.getSendMessageRepository());
@@ -500,27 +540,27 @@ public OnNextMessageUseCase createOnNextMessageUseCase() {
@NonNull
public SendMessageButtonStateUseCase createEnableSendMessageButtonUseCase() {
return new SendMessageButtonStateUseCase(
- repositoryFactory.getSendMessageRepository(),
- repositoryFactory.getSecureFileAttachmentRepository(),
- repositoryFactory.getSecureConversationsRepository(),
- createShowMessageLimitErrorUseCase(),
- schedulers
+ repositoryFactory.getSendMessageRepository(),
+ repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getSecureConversationsRepository(),
+ createShowMessageLimitErrorUseCase(),
+ schedulers
);
}
@NonNull
public ShowMessageLimitErrorUseCase createShowMessageLimitErrorUseCase() {
return new ShowMessageLimitErrorUseCase(
- repositoryFactory.getSendMessageRepository(),
- schedulers
+ repositoryFactory.getSendMessageRepository(),
+ schedulers
);
}
@NonNull
public ResetMessageCenterUseCase createResetMessageCenterUseCase() {
return new ResetMessageCenterUseCase(
- repositoryFactory.getSecureFileAttachmentRepository(),
- repositoryFactory.getSendMessageRepository()
+ repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getSendMessageRepository()
);
}
@@ -552,16 +592,16 @@ public RemoveVisitorMediaStateListenerUseCase createRemoveVisitorMediaStateListe
@NonNull
public ToggleVisitorAudioMediaMuteUseCase createToggleVisitorAudioMediaMuteUseCase() {
return new ToggleVisitorAudioMediaMuteUseCase(
- schedulers,
- repositoryFactory.getGliaVisitorMediaRepository()
+ schedulers,
+ repositoryFactory.getGliaVisitorMediaRepository()
);
}
@NonNull
public ToggleVisitorVideoUseCase createToggleVisitorVideoUseCase() {
return new ToggleVisitorVideoUseCase(
- schedulers,
- repositoryFactory.getGliaVisitorMediaRepository()
+ schedulers,
+ repositoryFactory.getGliaVisitorMediaRepository()
);
}
@@ -648,12 +688,12 @@ public RemoveMediaUpgradeOfferCallbackUseCase createRemoveMediaUpgradeOfferCallb
@NonNull
public SendSecureMessageUseCase createSendSecureMessageUseCase(String queueId) {
return new SendSecureMessageUseCase(
- queueId,
- repositoryFactory.getSendMessageRepository(),
- repositoryFactory.getSecureConversationsRepository(),
- repositoryFactory.getSecureFileAttachmentRepository(),
- repositoryFactory.getGliaMessageRepository(),
- repositoryFactory.getGliaEngagementRepository()
+ queueId,
+ repositoryFactory.getSendMessageRepository(),
+ repositoryFactory.getSecureConversationsRepository(),
+ repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getGliaMessageRepository(),
+ repositoryFactory.getGliaEngagementRepository()
);
}
@@ -670,8 +710,8 @@ public AddSecureFileToAttachmentAndUploadUseCase createAddSecureFileToAttachment
@NonNull
public AddSecureFileAttachmentsObserverUseCase createAddSecureFileAttachmentsObserverUseCase() {
return new AddSecureFileAttachmentsObserverUseCase(
- repositoryFactory.getSecureFileAttachmentRepository(),
- schedulers
+ repositoryFactory.getSecureFileAttachmentRepository(),
+ schedulers
);
}
@@ -688,9 +728,9 @@ public RemoveSecureFileAttachmentUseCase createRemoveSecureFileAttachmentUseCase
@NonNull
public IsSecureEngagementUseCase createIsSecureEngagementUseCase() {
return new IsSecureEngagementUseCase(
- repositoryFactory.getEngagementConfigRepository(),
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaQueueRepository()
+ repositoryFactory.getEngagementConfigRepository(),
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaQueueRepository()
);
}
@@ -702,8 +742,8 @@ public IsAuthenticatedUseCase createIsAuthenticatedUseCase() {
@NonNull
public SetEngagementConfigUseCase createSetEngagementConfigUseCase() {
return new SetEngagementConfigUseCase(
- repositoryFactory.getEngagementConfigRepository(),
- createResetSurveyUseCase()
+ repositoryFactory.getEngagementConfigRepository(),
+ createResetSurveyUseCase()
);
}
@@ -725,8 +765,8 @@ public IsMessagingAvailableUseCase createIsMessagingAvailableUseCase() {
@NonNull
public IsSecureConversationsChatAvailableUseCase createIsSecureConversationsChatAvailableUseCase() {
return new IsSecureConversationsChatAvailableUseCase(
- repositoryFactory.getEngagementConfigRepository(),
- createIsMessagingAvailableUseCase()
+ repositoryFactory.getEngagementConfigRepository(),
+ createIsMessagingAvailableUseCase()
);
}
@@ -743,24 +783,24 @@ public MarkMessagesReadWithDelayUseCase createMarkMessagesReadUseCase() {
@NonNull
public GliaOnCallVisualizerUseCase createOnCallVisualizerUseCase() {
return new GliaOnCallVisualizerUseCase(
- repositoryFactory.getGliaEngagementRepository(),
- repositoryFactory.getGliaOperatorMediaRepository(),
- repositoryFactory.getGliaQueueRepository(),
- repositoryFactory.getGliaVisitorMediaRepository(),
- repositoryFactory.getGliaEngagementStateRepository()
+ repositoryFactory.getGliaEngagementRepository(),
+ repositoryFactory.getGliaOperatorMediaRepository(),
+ repositoryFactory.getGliaQueueRepository(),
+ repositoryFactory.getGliaVisitorMediaRepository(),
+ repositoryFactory.getGliaEngagementStateRepository()
);
}
@NonNull
public GliaOnCallVisualizerEndUseCase createOnCallVisualizerEndUseCase() {
return new GliaOnCallVisualizerEndUseCase(
- repositoryFactory.getCallVisualizerRepository(),
- repositoryFactory.getGliaOperatorMediaRepository(),
- createOnCallVisualizerUseCase(),
- callNotificationUseCase,
- removeScreenSharingNotificationUseCase,
- repositoryFactory.getGliaSurveyRepository(),
- repositoryFactory.getGliaVisitorMediaRepository()
+ repositoryFactory.getCallVisualizerRepository(),
+ repositoryFactory.getGliaOperatorMediaRepository(),
+ createOnCallVisualizerUseCase(),
+ callNotificationUseCase,
+ removeScreenSharingNotificationUseCase,
+ repositoryFactory.getGliaSurveyRepository(),
+ repositoryFactory.getGliaVisitorMediaRepository()
);
}
@@ -792,34 +832,209 @@ public ResetSurveyUseCase createResetSurveyUseCase() {
@NonNull
public OnAudioStartedUseCase createOnAudioStartedUseCase() {
return new OnAudioStartedUseCase(
- repositoryFactory.getGliaOperatorMediaRepository(),
- repositoryFactory.getGliaVisitorMediaRepository()
+ repositoryFactory.getGliaOperatorMediaRepository(),
+ repositoryFactory.getGliaVisitorMediaRepository()
);
}
@NonNull
public TurnSpeakerphoneUseCase createTurnSpeakerphoneUseCase() {
return new TurnSpeakerphoneUseCase(
- audioControlManager
+ audioControlManager
);
}
@NonNull
public AcceptMediaUpgradeOfferUseCase createAcceptMediaUpgradeOfferUseCase() {
return new AcceptMediaUpgradeOfferUseCase(
- repositoryFactory.getMediaUpgradeOfferRepository(),
- permissionManager
+ repositoryFactory.getMediaUpgradeOfferRepository(),
+ permissionManager
);
}
@NonNull
public HandleCallPermissionsUseCase createHandleCallPermissionsUseCase() {
return new HandleCallPermissionsUseCase(
- createIsCallVisualizerUseCase(),
- permissionManager
+ createIsCallVisualizerUseCase(),
+ permissionManager
+ );
+ }
+
+ @NonNull
+ private Gson getGvaGson() {
+ if (gvaGson == null) {
+ gvaGson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
+ }
+
+ return gvaGson;
+ }
+
+ @NonNull
+ public ParseGvaButtonsUseCase createParseGvaButtonsUseCase() {
+ return new ParseGvaButtonsUseCase(getGvaGson());
+ }
+
+ @NonNull
+ public ParseGvaGalleryCardsUseCase createParseGvaGalleryCardsUseCase() {
+ return new ParseGvaGalleryCardsUseCase(getGvaGson());
+ }
+
+ @NonNull
+ public GetGvaTypeUseCase createGetGvaTypeUseCase() {
+ return new GetGvaTypeUseCase();
+ }
+
+ @NonNull
+ public IsGvaUseCase createIsGvaUseCase() {
+ return new IsGvaUseCase(createGetGvaTypeUseCase());
+ }
+
+ @NonNull
+ public MapGvaResponseTextUseCase createMapGvaResponseTextUseCase() {
+ return new MapGvaResponseTextUseCase();
+ }
+
+ @NonNull
+ public MapGvaPersistentButtonsUseCase createMapGvaPersistentButtonsUseCase() {
+ return new MapGvaPersistentButtonsUseCase(createParseGvaButtonsUseCase());
+ }
+
+ @NonNull
+ public MapGvaQuickRepliesUseCase createMapGvaGvaQuickRepliesUseCase() {
+ return new MapGvaQuickRepliesUseCase(createParseGvaButtonsUseCase());
+ }
+
+ @NonNull
+ public MapGvaGvaGalleryCardsUseCase createMapGvaGvaGalleryCardsUseCase() {
+ return new MapGvaGvaGalleryCardsUseCase(createParseGvaGalleryCardsUseCase());
+ }
+
+ @NonNull
+ public MapGvaUseCase createMapGvaUseCase() {
+ return new MapGvaUseCase(
+ createGetGvaTypeUseCase(),
+ createMapGvaResponseTextUseCase(),
+ createMapGvaPersistentButtonsUseCase(),
+ createMapGvaGvaQuickRepliesUseCase(),
+ createMapGvaGvaGalleryCardsUseCase()
);
}
+ @NonNull
+ public DetermineGvaUrlTypeUseCase createDetermineGvaUrlTypeUseCase() {
+ return new DetermineGvaUrlTypeUseCase();
+ }
+
+ @NonNull
+ public DetermineGvaButtonTypeUseCase createDetermineGvaButtonTypeUseCase() {
+ return new DetermineGvaButtonTypeUseCase(createDetermineGvaUrlTypeUseCase());
+ }
+
+ @NonNull
+ public HandleCustomCardClickUseCase createHandleCustomCardClickUseCase() {
+ return new HandleCustomCardClickUseCase(
+ createCustomCardTypeUseCase(),
+ createCustomCardShouldShowUseCase()
+ );
+ }
+
+ @NonNull
+ public AppendHistoryChatMessageUseCase createAppendHistoryChatMessageUseCase() {
+ return new AppendHistoryChatMessageUseCase(
+ createAppendHistoryVisitorChatItemUseCase(),
+ createAppendHistoryOperatorChatItemUseCase(),
+ createAppendSystemMessageItemUseCase()
+ );
+ }
+
+ @NonNull
+ public AppendSystemMessageItemUseCase createAppendSystemMessageItemUseCase() {
+ return new AppendSystemMessageItemUseCase();
+ }
+
+ @NonNull
+ public AppendHistoryVisitorChatItemUseCase createAppendHistoryVisitorChatItemUseCase() {
+ return new AppendHistoryVisitorChatItemUseCase(createMapVisitorAttachmentUseCase());
+ }
+
+ @NonNull
+ public MapVisitorAttachmentUseCase createMapVisitorAttachmentUseCase() {
+ return new MapVisitorAttachmentUseCase();
+ }
+
+ @NonNull
+ public AppendHistoryOperatorChatItemUseCase createAppendHistoryOperatorChatItemUseCase() {
+ return new AppendHistoryOperatorChatItemUseCase(
+ createIsGvaUseCase(),
+ createCustomCardAdapterTypeUseCase(),
+ createAppendHistoryGvaMessageItemUseCase(),
+ createAppendHistoryCustomCardItemUseCase(),
+ createAppendHistoryResponseCardOrTextItemUseCase()
+ );
+ }
+
+ @NonNull
+ public AppendHistoryCustomCardItemUseCase createAppendHistoryCustomCardItemUseCase() {
+ return new AppendHistoryCustomCardItemUseCase(
+ createCustomCardTypeUseCase(),
+ createCustomCardShouldShowUseCase()
+ );
+ }
+
+ @NonNull
+ public AppendGvaMessageItemUseCase createAppendHistoryGvaMessageItemUseCase() {
+ return new AppendGvaMessageItemUseCase(createMapGvaUseCase());
+ }
+
+ @NonNull
+ public AppendNewVisitorMessageUseCase createAppendNewVisitorMessageUseCase() {
+ return new AppendNewVisitorMessageUseCase(createMapVisitorAttachmentUseCase());
+ }
+
+ @NonNull
+ public AppendNewOperatorMessageUseCase createAppendNewOperatorMessageUseCase() {
+ return new AppendNewOperatorMessageUseCase(
+ createIsGvaUseCase(),
+ createCustomCardAdapterTypeUseCase(),
+ createAppendGvaMessageItemUseCase(),
+ createAppendHistoryCustomCardItemUseCase(),
+ createAppendNewResponseCardOrTextItemUseCase()
+ );
+ }
+
+ @NonNull
+ private AppendNewResponseCardOrTextItemUseCase createAppendNewResponseCardOrTextItemUseCase() {
+ return new AppendNewResponseCardOrTextItemUseCase(
+ createMapOperatorAttachmentUseCase(),
+ createMapOperatorPlainTextUseCase(),
+ createMapResponseCardUseCase()
+ );
+ }
+
+ @NonNull
+ public AppendGvaMessageItemUseCase createAppendGvaMessageItemUseCase() {
+ return new AppendGvaMessageItemUseCase(createMapGvaUseCase());
+ }
+
+ @NonNull
+ public AppendNewChatMessageUseCase createAppendNewChatMessageUseCase() {
+ return new AppendNewChatMessageUseCase(
+ createAppendNewOperatorMessageUseCase(),
+ createAppendNewVisitorMessageUseCase(),
+ createAppendSystemMessageItemUseCase()
+ );
+ }
+
+ @NonNull
+ public SendUnsentMessagesUseCase createSendUnsentMessagesUseCase() {
+ return new SendUnsentMessagesUseCase(repositoryFactory.getGliaMessageRepository());
+ }
+
+ @NonNull
+ public UpdateOperatorDefaultImageUrlUseCase createUpdateOperatorDefaultImageUrlUseCase() {
+ return new UpdateOperatorDefaultImageUrlUseCase(repositoryFactory.getOperatorRepository(), createSiteInfoUseCase());
+ }
+
public void resetState() {
createResetSurveyUseCase().invoke();
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
index cc2fff4ed..94f786cb0 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
@@ -14,6 +14,7 @@ import com.glia.widgets.helper.fileName
import io.reactivex.Completable
import io.reactivex.Maybe
import java.io.InputStream
+import kotlin.jvm.optionals.getOrNull
internal class GliaFileRepositoryImpl(
private val bitmapCache: InAppBitmapCache,
@@ -67,7 +68,7 @@ internal class GliaFileRepositoryImpl(
}
private fun fetchFile(attachmentFile: AttachmentFile, callback: RequestCallback) {
- val engagement = gliaCore.currentEngagement.orElse(null)
+ val engagement = gliaCore.currentEngagement.getOrNull()
if (engagement == null && engagementConfigRepository.chatType == ChatType.SECURE_MESSAGING) {
secureConversations.fetchFile(attachmentFile.id, callback)
} else {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
index 866616af9..40cbf3cbe 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
@@ -2,38 +2,52 @@ package com.glia.widgets.helper
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
+import android.text.Html
+import android.text.Spanned
import android.text.format.DateUtils
import androidx.annotation.ColorInt
import androidx.core.graphics.drawable.DrawableCompat
import com.glia.androidsdk.Engagement
import com.glia.androidsdk.Operator
+import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.androidsdk.chat.MessageAttachment
+import com.glia.androidsdk.chat.OperatorMessage
+import com.glia.androidsdk.chat.SingleChoiceAttachment
import com.glia.androidsdk.queuing.Queue
import com.glia.widgets.UiTheme
+import com.glia.widgets.core.engagement.data.LocalOperator
+import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
import com.glia.widgets.view.unifiedui.deepMerge
+import kotlin.jvm.optionals.getOrNull
internal fun Drawable.setTintCompat(@ColorInt color: Int) = DrawableCompat.setTint(this, color)
@ColorInt
-internal fun ColorStateList?.colorForState(state: IntArray): Int? =
- this?.getColorForState(state, defaultColor)
+internal fun ColorStateList?.colorForState(state: IntArray): Int? = this?.getColorForState(state, defaultColor)
// Common
-internal fun String.separateStringWithSymbol(symbol: String): String =
- asSequence().joinToString(symbol)
+internal fun String.separateStringWithSymbol(symbol: String): String = asSequence().joinToString(symbol)
-internal fun Queue.supportsMessaging() = state.medias.contains(Engagement.MediaType.MESSAGING)
+internal fun Queue.supportMessaging() = state.medias.contains(Engagement.MediaType.MESSAGING)
-internal val Operator.imageUrl: String?
- get() = picture?.url?.orElse(null)
+internal fun formatElapsedTime(elapsedMilliseconds: Long) = DateUtils.formatElapsedTime(elapsedMilliseconds / DateUtils.SECOND_IN_MILLIS)
-internal fun formatElapsedTime(elapsedMilliseconds: Long) =
- DateUtils.formatElapsedTime(elapsedMilliseconds / DateUtils.SECOND_IN_MILLIS)
+internal val Operator.formattedName: String get() = name.substringBefore(' ')
-internal val Operator.formattedName: String
- get() = name.substringBefore(' ')
+internal val Operator.imageUrl: String? get() = picture?.url?.getOrNull()
-internal fun UiTheme?.isAlertDialogButtonUseVerticalAlignment(): Boolean =
- this?.gliaAlertDialogButtonUseVerticalAlignment ?: false
+internal fun UiTheme?.isAlertDialogButtonUseVerticalAlignment(): Boolean = this?.gliaAlertDialogButtonUseVerticalAlignment ?: false
-internal fun UiTheme?.getFullHybridTheme(newTheme: UiTheme?): UiTheme =
- deepMerge(newTheme) ?: UiTheme.UiThemeBuilder().build()
+internal fun UiTheme?.getFullHybridTheme(newTheme: UiTheme?): UiTheme = deepMerge(newTheme) ?: UiTheme.UiThemeBuilder().build()
+
+/**
+ * Returns styled text from the provided HTML string. Replaces \n to
regardless of the operating system where the string was created.
+ */
+internal fun String.fromHtml(flags: Int = Html.FROM_HTML_MODE_COMPACT): Spanned = Html.fromHtml(replace("(\r\n|\n)".toRegex(), "
"), flags)
+
+internal val AttachmentFile.isImage: Boolean get() = contentType.startsWith("image")
+
+internal fun MessageAttachment.asSingleChoice(): SingleChoiceAttachment? = this as? SingleChoiceAttachment
+
+internal fun OperatorMessage.toChatMessageInternal(): ChatMessageInternal =
+ ChatMessageInternal(this, LocalOperator(operatorId.orEmpty(), operatorName.orEmpty(), operatorImageUrl))
diff --git a/widgetssdk/src/main/java/com/glia/widgets/survey/viewholder/SingleQuestionViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/survey/viewholder/SingleQuestionViewHolder.kt
index a22180880..b38b67510 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/survey/viewholder/SingleQuestionViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/survey/viewholder/SingleQuestionViewHolder.kt
@@ -21,6 +21,7 @@ import com.glia.widgets.view.configuration.survey.SurveyStyle
import com.glia.widgets.view.unifiedui.applyTextTheme
import com.glia.widgets.view.unifiedui.theme.survey.SurveySingleQuestionTheme
import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
class SingleQuestionViewHolder(
private val binding: SurveySingleQuestionItemBinding,
@@ -50,7 +51,7 @@ class SingleQuestionViewHolder(
private fun singleChoice(item: QuestionItem) {
val selectedId = Optional.ofNullable(item.answer)
.map { answer: Survey.Answer -> answer.getResponse() as String }
- .orElse(null)
+ .getOrNull()
val options = item.question.options ?: return
radioGroup.removeAllViews()
for (i in options.indices) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/Dialogs.kt b/widgetssdk/src/main/java/com/glia/widgets/view/Dialogs.kt
index 64a771a8b..8f80010a1 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/Dialogs.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/Dialogs.kt
@@ -181,7 +181,7 @@ object Dialogs {
text = negativeButtonText
setOnClickListener(negativeButtonClickListener)
applyButtonTheme(
- backgroundColor = systemNegativeColor,
+ backgroundColor = if (isButtonsColorsReversed) { brandPrimaryColor } else { systemNegativeColor },
textColor = baseLightColor,
textFont = fontFamily
)
@@ -191,7 +191,7 @@ object Dialogs {
text = positiveButtonText
setOnClickListener(positiveButtonClickListener)
applyButtonTheme(
- backgroundColor = brandPrimaryColor,
+ backgroundColor = if (isButtonsColorsReversed) { systemNegativeColor } else { brandPrimaryColor },
textColor = baseLightColor,
textFont = fontFamily
)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/MessagesNotSeenHandler.java b/widgetssdk/src/main/java/com/glia/widgets/view/MessagesNotSeenHandler.java
index f80da8526..4076539da 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/MessagesNotSeenHandler.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/MessagesNotSeenHandler.java
@@ -27,7 +27,7 @@ public MessagesNotSeenHandler(
public void init() {
Logger.d(TAG, "init");
- gliaOnMessageUseCase.execute().doOnNext(this::onMessage).subscribe();
+ gliaOnMessageUseCase.invoke().doOnNext(this::onMessage).subscribe();
}
public void chatOnBackClicked() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/SingleChoiceCardView.kt b/widgetssdk/src/main/java/com/glia/widgets/view/SingleChoiceCardView.kt
index 549c5969f..cee0d8e02 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/SingleChoiceCardView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/SingleChoiceCardView.kt
@@ -14,7 +14,7 @@ import androidx.core.view.updatePadding
import com.glia.androidsdk.chat.SingleChoiceOption
import com.glia.widgets.R
import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.model.history.ResponseCardItem
+import com.glia.widgets.chat.model.OperatorMessageItem
import com.glia.widgets.databinding.SingleChoiceCardViewBinding
import com.glia.widgets.di.Dependencies
import com.glia.widgets.helper.Utils
@@ -64,22 +64,22 @@ class SingleChoiceCardView @JvmOverloads constructor(
bgDrawable.setStroke(strokeSize, color)
}
- fun setData(
- item: ResponseCardItem,
+ internal fun setData(
+ item: OperatorMessageItem.ResponseCard,
theme: UiTheme
) {
setupCardView(theme)
setupImage(item.choiceCardImageUrl)
- setupText(item.content, theme)
+ setupText(item.content.orEmpty(), theme)
setupButtons(item, theme)
}
- fun setOnOptionClickedListener(onOptionClickedListener: OnOptionClickedListener?) {
+ internal fun setOnOptionClickedListener(onOptionClickedListener: OnOptionClickedListener?) {
this.onOptionClickedListener = onOptionClickedListener
}
- fun interface OnOptionClickedListener {
- fun onClicked(item: ResponseCardItem, selectedOption: SingleChoiceOption)
+ internal fun interface OnOptionClickedListener {
+ fun onClicked(item: OperatorMessageItem.ResponseCard, selectedOption: SingleChoiceOption)
}
private fun setupCardView(theme: UiTheme) {
@@ -131,7 +131,7 @@ class SingleChoiceCardView @JvmOverloads constructor(
}
private fun setupButtons(
- item: ResponseCardItem,
+ item: OperatorMessageItem.ResponseCard,
theme: UiTheme
) {
val horizontalMargin = resources.getDimensionPixelOffset(R.dimen.glia_large)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/head/controller/ApplicationChatHeadLayoutController.kt b/widgetssdk/src/main/java/com/glia/widgets/view/head/controller/ApplicationChatHeadLayoutController.kt
index da4ece5c6..e881d35e1 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/head/controller/ApplicationChatHeadLayoutController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/head/controller/ApplicationChatHeadLayoutController.kt
@@ -187,14 +187,11 @@ internal class ApplicationChatHeadLayoutController(
state = State.ENGAGEMENT
engagementDisposables.add(
getOperatorFlowableUseCase.execute()
- .subscribe(
- { operator: Operator -> operatorDataLoaded(operator) }
- ) { throwable: Throwable ->
- Logger.e(TAG, "getOperatorFlowableUseCase error: " + throwable.message)
- }
+ .subscribe({ operatorDataLoaded(it) }) { Logger.e(TAG, "getOperatorFlowableUseCase error: " + it.message) }
)
updateChatHeadView()
}
+
override fun newEngagementLoaded(engagement: OmnibrowseEngagement) {
onNewEngagementLoaded()
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/Merge.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/Merge.kt
index 98a9fef81..944edaa99 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/Merge.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/Merge.kt
@@ -26,7 +26,7 @@ import kotlin.reflect.full.primaryConstructor
* val c = a deepMerge b
*/
internal inline infix fun T.deepMerge(other: T): T {
- if (!T::class.isData) throw UnsupportedOperationException("Merge supports only data classes")
+ if (!T::class.isData) throw UnsupportedOperationException("Merge supports only data classes, ${T::class.simpleName} is not a data class")
return unsafeMerge(other)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/UnifiedUiExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/UnifiedUiExtensions.kt
index 36bded1e6..223d738b5 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/UnifiedUiExtensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/UnifiedUiExtensions.kt
@@ -25,7 +25,9 @@ import com.glia.widgets.view.unifiedui.theme.survey.OptionButtonTheme
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.progressindicator.CircularProgressIndicator
+import com.google.android.material.shape.CornerFamily
internal fun View.applyColorTheme(color: ColorTheme?) {
background = createBackgroundFromTheme(color ?: return)
@@ -74,6 +76,25 @@ internal fun View.applyLayerTheme(layer: LayerTheme?) {
background = drawable
}
+internal fun ShapeableImageView.applyLayerTheme(layer: LayerTheme?) {
+ layer?.fill?.also {
+ val drawable = (background as? GradientDrawable) ?: GradientDrawable()
+ if (it.isGradient) {
+ drawable.colors = it.valuesArray
+ } else {
+ drawable.setColor(it.primaryColor)
+ }
+ background = drawable
+ }
+
+ layer?.stroke?.also { strokeColor = ColorStateList.valueOf(it) }
+ layer?.borderWidth?.also(::setStrokeWidth)
+
+ layer?.cornerRadius?.also {
+ shapeAppearanceModel = shapeAppearanceModel.toBuilder().setAllCorners(CornerFamily.ROUNDED, it).build()
+ }
+}
+
internal fun MaterialCardView.applyCardLayerTheme(layer: LayerTheme?) {
layer?.fill?.primaryColor?.also {
/*
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/chat/ChatRemoteConfig.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/chat/ChatRemoteConfig.kt
index b192ffadf..5eb49170e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/chat/ChatRemoteConfig.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/chat/ChatRemoteConfig.kt
@@ -5,6 +5,7 @@ import com.glia.widgets.view.unifiedui.config.base.HeaderRemoteConfig
import com.glia.widgets.view.unifiedui.config.base.LayerRemoteConfig
import com.glia.widgets.view.unifiedui.config.base.TextRemoteConfig
import com.glia.widgets.view.unifiedui.config.bubble.BubbleRemoteConfig
+import com.glia.widgets.view.unifiedui.config.gva.GvaRemoteConfig
import com.glia.widgets.view.unifiedui.theme.chat.ChatTheme
import com.google.gson.annotations.SerializedName
@@ -52,7 +53,10 @@ internal data class ChatRemoteConfig(
val newMessagesDividerColorRemoteConfig: ColorLayerRemoteConfig?,
@SerializedName("newMessagesDividerText")
- val newMessagesDividerTextRemoteConfig: TextRemoteConfig?
+ val newMessagesDividerTextRemoteConfig: TextRemoteConfig?,
+
+ @SerializedName("gva")
+ val gvaRemoteConfig: GvaRemoteConfig?
) {
fun toChatTheme(): ChatTheme = ChatTheme(
background = background?.toLayerTheme(),
@@ -69,6 +73,7 @@ internal data class ChatRemoteConfig(
unreadIndicator = unreadIndicator?.toUnreadIndicatorTheme(),
typingIndicator = typingIndicator?.toColorTheme(),
newMessagesDividerColorTheme = newMessagesDividerColorRemoteConfig?.toColorTheme(),
- newMessagesDividerTextTheme = newMessagesDividerTextRemoteConfig?.toTextTheme()
+ newMessagesDividerTextTheme = newMessagesDividerTextRemoteConfig?.toTextTheme(),
+ gva = gvaRemoteConfig?.toGvaTheme()
)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaGalleryCardRemoteConfig.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaGalleryCardRemoteConfig.kt
new file mode 100644
index 000000000..b4297a113
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaGalleryCardRemoteConfig.kt
@@ -0,0 +1,32 @@
+package com.glia.widgets.view.unifiedui.config.gva
+
+import com.glia.widgets.view.unifiedui.config.base.ButtonRemoteConfig
+import com.glia.widgets.view.unifiedui.config.base.LayerRemoteConfig
+import com.glia.widgets.view.unifiedui.config.base.TextRemoteConfig
+import com.glia.widgets.view.unifiedui.theme.gva.GvaGalleryCardTheme
+import com.google.gson.annotations.SerializedName
+
+internal data class GvaGalleryCardRemoteConfig(
+ @SerializedName("title")
+ val titleRemoteConfig: TextRemoteConfig?,
+
+ @SerializedName("subtitle")
+ val subtitleRemoteConfig: TextRemoteConfig?,
+
+ @SerializedName("image")
+ val imageRemoteConfig: LayerRemoteConfig?,
+
+ @SerializedName("button")
+ val buttonRemoteConfig: ButtonRemoteConfig?,
+
+ @SerializedName("background")
+ val backgroundRemoteConfig: LayerRemoteConfig?
+) {
+ fun toGvaGalleryCardTheme(): GvaGalleryCardTheme = GvaGalleryCardTheme(
+ title = titleRemoteConfig?.toTextTheme(),
+ subtitle = subtitleRemoteConfig?.toTextTheme(),
+ image = imageRemoteConfig?.toLayerTheme(),
+ button = buttonRemoteConfig?.toButtonTheme(),
+ background = backgroundRemoteConfig?.toLayerTheme()
+ )
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaPersistentButtonRemoteConfig.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaPersistentButtonRemoteConfig.kt
new file mode 100644
index 000000000..0e01d541e
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaPersistentButtonRemoteConfig.kt
@@ -0,0 +1,24 @@
+package com.glia.widgets.view.unifiedui.config.gva
+
+import com.glia.widgets.view.unifiedui.config.base.ButtonRemoteConfig
+import com.glia.widgets.view.unifiedui.config.base.LayerRemoteConfig
+import com.glia.widgets.view.unifiedui.config.base.TextRemoteConfig
+import com.glia.widgets.view.unifiedui.theme.gva.GvaPersistentButtonTheme
+import com.google.gson.annotations.SerializedName
+
+internal data class GvaPersistentButtonRemoteConfig(
+ @SerializedName("title")
+ val titleRemoteConfig: TextRemoteConfig?,
+
+ @SerializedName("background")
+ val backgroundRemoteConfig: LayerRemoteConfig?,
+
+ @SerializedName("button")
+ val buttonRemoteConfig: ButtonRemoteConfig?
+) {
+ fun toGvaPersistentButtonTheme(): GvaPersistentButtonTheme = GvaPersistentButtonTheme(
+ title = titleRemoteConfig?.toTextTheme(),
+ background = backgroundRemoteConfig?.toLayerTheme(),
+ button = buttonRemoteConfig?.toButtonTheme()
+ )
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaRemoteConfig.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaRemoteConfig.kt
new file mode 100644
index 000000000..7db26dd9b
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/config/gva/GvaRemoteConfig.kt
@@ -0,0 +1,22 @@
+package com.glia.widgets.view.unifiedui.config.gva
+
+import com.glia.widgets.view.unifiedui.config.base.ButtonRemoteConfig
+import com.glia.widgets.view.unifiedui.theme.gva.GvaTheme
+import com.google.gson.annotations.SerializedName
+
+internal data class GvaRemoteConfig(
+ @SerializedName("quickReplyButton")
+ val quickReplyRemoteConfig: ButtonRemoteConfig?,
+
+ @SerializedName("persistentButton")
+ val persistentButtonRemoteConfig: GvaPersistentButtonRemoteConfig?,
+
+ @SerializedName("galleryCard")
+ val galleryCardRemoteConfig: GvaGalleryCardRemoteConfig?
+) {
+ fun toGvaTheme(): GvaTheme = GvaTheme(
+ quickReplyTheme = quickReplyRemoteConfig?.toButtonTheme(),
+ persistentButtonTheme = persistentButtonRemoteConfig?.toGvaPersistentButtonTheme(),
+ galleryCardTheme = galleryCardRemoteConfig?.toGvaGalleryCardTheme()
+ )
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/parse/RemoteConfigurationParser.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/parse/RemoteConfigurationParser.kt
index 07a5161a0..49f483676 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/parse/RemoteConfigurationParser.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/parse/RemoteConfigurationParser.kt
@@ -1,6 +1,7 @@
package com.glia.widgets.view.unifiedui.parse
import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.ResourceProvider
import com.glia.widgets.view.unifiedui.config.RemoteConfiguration
import com.glia.widgets.view.unifiedui.config.alert.AxisRemoteConfig
import com.glia.widgets.view.unifiedui.config.base.AlignmentTypeRemoteConfig
@@ -13,7 +14,9 @@ import com.glia.widgets.view.unifiedui.config.chat.AttachmentSourceTypeRemoteCon
import com.google.gson.Gson
import com.google.gson.GsonBuilder
-internal class RemoteConfigurationParser {
+internal class RemoteConfigurationParser(
+ resourceProvider: ResourceProvider = Dependencies.getResourceProvider()
+) {
/**
* @return [Gson] instance with applied deserializers to parse remote config.
*/
@@ -23,7 +26,7 @@ internal class RemoteConfigurationParser {
.registerTypeAdapter(ColorLayerRemoteConfig::class.java, ColorLayerDeserializer())
.registerTypeAdapter(
SizeDpRemoteConfig::class.java,
- DpDeserializer(Dependencies.getResourceProvider())
+ DpDeserializer(resourceProvider)
)
.registerTypeAdapter(SizeSpRemoteConfig::class.java, SpDeserializer())
.registerTypeAdapter(TextStyleRemoteConfig::class.java, TextStyleDeserializer())
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/base/LayerTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/base/LayerTheme.kt
index 5126b1f9c..cfe36b42b 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/base/LayerTheme.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/base/LayerTheme.kt
@@ -8,7 +8,7 @@ internal data class LayerTheme(
val fill: ColorTheme? = null,
@ColorInt
val stroke: Int? = null,
- // Currently it is not possible to draw gradient stroke(change to ThemeColor in case of migrating to Jetpack Compose)
+ // Currently, it is impossible to draw a gradient stroke(change to ThemeColor in case of migrating to Jetpack Compose)
@Px
val borderWidth: Float? = null, // width in pixels
@Px
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/chat/ChatTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/chat/ChatTheme.kt
index cb99e79fa..afbd1e812 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/chat/ChatTheme.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/chat/ChatTheme.kt
@@ -5,6 +5,7 @@ import com.glia.widgets.view.unifiedui.theme.base.HeaderTheme
import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
import com.glia.widgets.view.unifiedui.theme.base.TextTheme
import com.glia.widgets.view.unifiedui.theme.bubble.BubbleTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaTheme
internal data class ChatTheme(
val background: LayerTheme? = null,
@@ -21,5 +22,6 @@ internal data class ChatTheme(
val unreadIndicator: UnreadIndicatorTheme? = null,
val typingIndicator: ColorTheme? = null,
val newMessagesDividerColorTheme: ColorTheme? = null,
- val newMessagesDividerTextTheme: TextTheme? = null
+ val newMessagesDividerTextTheme: TextTheme? = null,
+ val gva: GvaTheme? = null
)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Button.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Button.kt
index 912551efe..708332c8d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Button.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Button.kt
@@ -29,6 +29,29 @@ internal fun NegativeDefaultButtonTheme(pallet: ColorPallet) = pallet.run {
DefaultButtonTheme(text = baseLightColorTheme, background = systemNegativeColorTheme)
}
+/**
+ * Default theme for Outlined Button
+ */
+internal fun OutlinedButtonTheme(
+ text: ColorTheme?,
+ stroke: ColorTheme?
+): ButtonTheme? = composeIfAtLeastOneNotNull(text, stroke) {
+ ButtonTheme(
+ text = TextTheme(textColor = text),
+ background = LayerTheme(stroke = stroke?.primaryColor),
+ iconColor = null,
+ elevation = null,
+ shadowColor = null
+ )
+}
+
+/**
+ * Default theme for GVA Button
+ */
+internal fun GvaDefaultButtonTheme(pallet: ColorPallet) = pallet.run {
+ DefaultButtonTheme(text = baseDarkColorTheme, background = backgroundColorTheme)
+}
+
private fun DefaultButtonTheme(
text: ColorTheme? = null,
background: ColorTheme? = null,
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Chat.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Chat.kt
index 5fc9bb893..500eb0427 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Chat.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Chat.kt
@@ -34,7 +34,8 @@ internal fun ChatTheme(pallet: ColorPallet): ChatTheme =
unreadIndicator = ChatUnreadIndicatorTheme(pallet),
typingIndicator = pallet.primaryColorTheme,
newMessagesDividerColorTheme = pallet.primaryColorTheme,
- newMessagesDividerTextTheme = TextTheme(textColor = pallet.primaryColorTheme)
+ newMessagesDividerTextTheme = TextTheme(textColor = pallet.primaryColorTheme),
+ gva = GvaTheme(pallet)
)
/**
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Gva.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Gva.kt
new file mode 100644
index 000000000..30093c2ed
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/defaulttheme/Gva.kt
@@ -0,0 +1,48 @@
+@file:Suppress("FunctionName")
+
+package com.glia.widgets.view.unifiedui.theme.defaulttheme
+
+import com.glia.widgets.view.unifiedui.composeIfAtLeastOneNotNull
+import com.glia.widgets.view.unifiedui.theme.ColorPallet
+import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaGalleryCardTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaPersistentButtonTheme
+import com.glia.widgets.view.unifiedui.theme.gva.GvaTheme
+
+/**
+ * Default Theme for Gva
+ */
+internal fun GvaTheme(pallet: ColorPallet): GvaTheme = GvaTheme(
+ quickReplyTheme = pallet.primaryColorTheme?.let { OutlinedButtonTheme(it, it) },
+ persistentButtonTheme = GvaPersistentButtonTheme(pallet),
+ galleryCardTheme = GvaGalleryCardTheme(pallet)
+)
+
+private fun GvaPersistentButtonTheme(
+ pallet: ColorPallet
+): GvaPersistentButtonTheme? = pallet.run {
+ composeIfAtLeastOneNotNull(baseLightColorTheme, baseDarkColorTheme, backgroundColorTheme) {
+ GvaPersistentButtonTheme(
+ title = BaseDarkColorTextTheme(this),
+ background = LayerTheme(
+ fill = baseLightColorTheme
+ ),
+ button = GvaDefaultButtonTheme(this)
+ )
+ }
+}
+
+private fun GvaGalleryCardTheme(
+ pallet: ColorPallet
+): GvaGalleryCardTheme? = pallet.run {
+ composeIfAtLeastOneNotNull(baseLightColorTheme, baseDarkColorTheme, backgroundColorTheme) {
+ GvaGalleryCardTheme(
+ title = BaseDarkColorTextTheme(this),
+ subtitle = BaseDarkColorTextTheme(this),
+ background = LayerTheme(
+ fill = baseLightColorTheme
+ ),
+ button = GvaDefaultButtonTheme(this)
+ )
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaGalleryCardTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaGalleryCardTheme.kt
new file mode 100644
index 000000000..ab00918be
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaGalleryCardTheme.kt
@@ -0,0 +1,13 @@
+package com.glia.widgets.view.unifiedui.theme.gva
+
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
+import com.glia.widgets.view.unifiedui.theme.base.TextTheme
+
+internal data class GvaGalleryCardTheme(
+ val title: TextTheme? = null,
+ val subtitle: TextTheme? = null,
+ val image: LayerTheme? = null,
+ val button: ButtonTheme? = null,
+ val background: LayerTheme? = null
+)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaPersistentButtonTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaPersistentButtonTheme.kt
new file mode 100644
index 000000000..7095182a7
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaPersistentButtonTheme.kt
@@ -0,0 +1,11 @@
+package com.glia.widgets.view.unifiedui.theme.gva
+
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
+import com.glia.widgets.view.unifiedui.theme.base.TextTheme
+
+internal data class GvaPersistentButtonTheme(
+ val title: TextTheme? = null,
+ val background: LayerTheme? = null,
+ val button: ButtonTheme? = null
+)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaTheme.kt
new file mode 100644
index 000000000..4fb076216
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/view/unifiedui/theme/gva/GvaTheme.kt
@@ -0,0 +1,9 @@
+package com.glia.widgets.view.unifiedui.theme.gva
+
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+
+internal data class GvaTheme(
+ val quickReplyTheme: ButtonTheme? = null,
+ val persistentButtonTheme: GvaPersistentButtonTheme? = null,
+ val galleryCardTheme: GvaGalleryCardTheme? = null
+)
diff --git a/widgetssdk/src/main/res/layout/chat_gva_gallery_item.xml b/widgetssdk/src/main/res/layout/chat_gva_gallery_item.xml
new file mode 100644
index 000000000..5ed6a14c4
--- /dev/null
+++ b/widgetssdk/src/main/res/layout/chat_gva_gallery_item.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/widgetssdk/src/main/res/layout/chat_gva_gallery_layout.xml b/widgetssdk/src/main/res/layout/chat_gva_gallery_layout.xml
new file mode 100644
index 000000000..98545d8cb
--- /dev/null
+++ b/widgetssdk/src/main/res/layout/chat_gva_gallery_layout.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/widgetssdk/src/main/res/layout/chat_gva_persistent_buttons_content.xml b/widgetssdk/src/main/res/layout/chat_gva_persistent_buttons_content.xml
new file mode 100644
index 000000000..75a740547
--- /dev/null
+++ b/widgetssdk/src/main/res/layout/chat_gva_persistent_buttons_content.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/widgetssdk/src/main/res/layout/chat_view.xml b/widgetssdk/src/main/res/layout/chat_view.xml
index bc064bdfa..5368fcd3e 100644
--- a/widgetssdk/src/main/res/layout/chat_view.xml
+++ b/widgetssdk/src/main/res/layout/chat_view.xml
@@ -17,12 +17,22 @@
android:id="@+id/chat_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:paddingBottom="@dimen/glia_medium"
android:clipToPadding="false"
- app:layout_constraintBottom_toTopOf="@+id/operator_typing_animation_view"
+ android:paddingBottom="@dimen/glia_medium"
+ app:layout_constraintBottom_toTopOf="@+id/gva_quick_replies_layout"
app:layout_constraintTop_toBottomOf="@+id/app_bar_view"
tools:listitem="@layout/chat_visitor_message_layout" />
+
+
+
@@ -115,13 +125,13 @@
android:layout_weight="1"
android:hint="@string/glia_chat_enter_message"
android:importantForAutofill="no"
- android:textCursorDrawable="@null"
+ android:maxLength="10000"
+ android:maxLines="4"
android:minHeight="@dimen/glia_chat_edit_text_min_height"
android:paddingStart="@dimen/glia_large"
android:textColor="?attr/gliaBaseDarkColor"
android:textColorHint="?attr/gliaBaseNormalColor"
- android:maxLines="4"
- android:maxLength="10000"
+ android:textCursorDrawable="@null"
tools:ignore="RtlSymmetry"
tools:textColor="@color/glia_black_color" />
@@ -153,6 +163,6 @@
android:id="@+id/group_chat_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:constraint_referenced_ids="operator_typing_animation_view, divider_view, add_attachment_queue, chat_message_layout"/>
+ app:constraint_referenced_ids="operator_typing_animation_view, divider_view, add_attachment_queue, chat_message_layout" />
diff --git a/widgetssdk/src/main/res/values/attrs.xml b/widgetssdk/src/main/res/values/attrs.xml
index 08667623a..2c0b4f032 100644
--- a/widgetssdk/src/main/res/values/attrs.xml
+++ b/widgetssdk/src/main/res/values/attrs.xml
@@ -84,9 +84,9 @@
-
+
-
+
@@ -174,9 +174,9 @@
-
+
-
+
@@ -208,6 +208,13 @@
+
+
+
+
+
+
+
diff --git a/widgetssdk/src/main/res/values/dimens.xml b/widgetssdk/src/main/res/values/dimens.xml
index 70384fbc6..527a7a5d6 100644
--- a/widgetssdk/src/main/res/values/dimens.xml
+++ b/widgetssdk/src/main/res/values/dimens.xml
@@ -58,6 +58,8 @@
18sp
6dp
+ 234dp
+
44dp
375dp
64dp
diff --git a/widgetssdk/src/main/res/values/strings.xml b/widgetssdk/src/main/res/values/strings.xml
index d7dce1e18..47abf28af 100644
--- a/widgetssdk/src/main/res/values/strings.xml
+++ b/widgetssdk/src/main/res/values/strings.xml
@@ -258,4 +258,8 @@
Could not load the visitor code. Please try refreshing.
Refresh
+
+ This action is not currently supported on mobile.
+ %1$s \n Card: %2$d of %3$d.
+
diff --git a/widgetssdk/src/main/res/values/styles.xml b/widgetssdk/src/main/res/values/styles.xml
index db2402dd4..6b85d7515 100644
--- a/widgetssdk/src/main/res/values/styles.xml
+++ b/widgetssdk/src/main/res/values/styles.xml
@@ -28,6 +28,11 @@
- @dimen/glia_x_large
+
+
+
+
+
+
+
+
+
+