diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index cccf70105..f64d7ca3b 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -45,6 +45,7 @@ import androidx.navigation.NavDeepLinkBuilder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async import kotlinx.coroutines.launch import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences @@ -158,16 +159,19 @@ class NotificationsManager @MainThread constructor(private val context: Context) if (ShortcutUtils.isShortcutToChatRoomAlreadyCreated(context, chatRoom)) { Log.i("$TAG Chat room shortcut already exists") + showChatRoomNotification(chatRoom, messages) } else { Log.i( - "$TAG Ensure chat room shortcut exists for bubble notification" + "$TAG Ensure chat room shortcut exists for 'conversation' notification" ) scope.launch { - ShortcutUtils.createShortcutsToChatRooms(context) + val shortcuts = async { + ShortcutUtils.createShortcutsToChatRooms(context) + } + shortcuts.await() + showChatRoomNotification(chatRoom, messages) } } - - showChatRoomNotification(chatRoom, messages) } @WorkerThread diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt index 99e60f74b..21b871390 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt @@ -46,14 +46,14 @@ class ChatMessageBottomSheetAdapter : ListAdapter + coreContext.postOnMainThread { + displayDeliveryStatuses(deliveryModel) + } } + bottomSheetDeliveryModel = model } } @UiThread private fun prepareBottomSheetForReactions(chatMessageModel: ChatMessageModel) { coreContext.postOnCoreThread { - val model = ChatMessageReactionsModel(chatMessageModel.chatMessage) - val totalCount = model.allReactions.size - val label = getString(R.string.message_reactions_info_all_title, totalCount.toString()) + bottomSheetReactionsModel?.destroy() - coreContext.postOnMainThread { - val tabs = binding.messageBottomSheet.tabs - tabs.removeAllTabs() - tabs.addTab( - tabs.newTab().setText(label).setId(0).setTag("") - ) - - var index = 1 - for (reaction in model.differentReactions.value.orEmpty()) { - val count = model.reactionsMap[reaction] - val tabLabel = getString( - R.string.message_reactions_info_emoji_title, - reaction, - count.toString() - ) - tabs.addTab( - tabs.newTab().setText(tabLabel).setId(index).setTag(reaction) - ) - index += 1 + val model = ChatMessageReactionsModel(chatMessageModel.chatMessage) { reactionsModel -> + coreContext.postOnMainThread { + if (reactionsModel.allReactions.isEmpty()) { + Log.i("$TAG No reaction to display, closing bottom sheet") + val bottomSheetBehavior = BottomSheetBehavior.from( + binding.messageBottomSheet.root + ) + bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + } else { + displayReactions(reactionsModel) + } } - - tabs.setOnTabSelectedListener(object : OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab?) { - val filter = tab?.tag.toString() - if (filter.isEmpty()) { - bottomSheetAdapter.submitList(model.allReactions) - } else { - bottomSheetAdapter.submitList(model.filterReactions(filter)) - } - } - - override fun onTabUnselected(tab: TabLayout.Tab?) { - } - - override fun onTabReselected(tab: TabLayout.Tab?) { - } - }) - - val initialList = model.allReactions - bottomSheetAdapter.submitList(initialList) - Log.i("$TAG Submitted [${initialList.size}] items for default reactions list") } + bottomSheetReactionsModel = model } } + + @UiThread + private fun displayDeliveryStatuses(model: ChatMessageDeliveryModel) { + val tabs = binding.messageBottomSheet.tabs + tabs.removeAllTabs() + tabs.addTab( + tabs.newTab().setText(model.readLabel.value).setId( + ChatMessage.State.Displayed.toInt() + ) + ) + tabs.addTab( + tabs.newTab().setText( + model.receivedLabel.value + ).setId( + ChatMessage.State.DeliveredToUser.toInt() + ) + ) + tabs.addTab( + tabs.newTab().setText(model.sentLabel.value).setId( + ChatMessage.State.Delivered.toInt() + ) + ) + tabs.addTab( + tabs.newTab().setText( + model.errorLabel.value + ).setId( + ChatMessage.State.NotDelivered.toInt() + ) + ) + + tabs.setOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + val state = tab?.id ?: ChatMessage.State.Displayed.toInt() + bottomSheetAdapter.submitList( + model.computeListForState(ChatMessage.State.fromInt(state)) + ) + } + + override fun onTabUnselected(tab: TabLayout.Tab?) { + } + + override fun onTabReselected(tab: TabLayout.Tab?) { + } + }) + + val initialList = model.displayedModels + bottomSheetAdapter.submitList(initialList) + Log.i("$TAG Submitted [${initialList.size}] items for default delivery status list") + } + + @UiThread + private fun displayReactions(model: ChatMessageReactionsModel) { + val totalCount = model.allReactions.size + val label = getString(R.string.message_reactions_info_all_title, totalCount.toString()) + + val tabs = binding.messageBottomSheet.tabs + tabs.removeAllTabs() + tabs.addTab( + tabs.newTab().setText(label).setId(0).setTag("") + ) + + var index = 1 + for (reaction in model.differentReactions.value.orEmpty()) { + val count = model.reactionsMap[reaction] + val tabLabel = getString( + R.string.message_reactions_info_emoji_title, + reaction, + count.toString() + ) + tabs.addTab( + tabs.newTab().setText(tabLabel).setId(index).setTag(reaction) + ) + index += 1 + } + + tabs.setOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + val filter = tab?.tag.toString() + if (filter.isEmpty()) { + bottomSheetAdapter.submitList(model.allReactions) + } else { + bottomSheetAdapter.submitList(model.filterReactions(filter)) + } + } + + override fun onTabUnselected(tab: TabLayout.Tab?) { + } + + override fun onTabReselected(tab: TabLayout.Tab?) { + } + }) + + val initialList = model.allReactions + bottomSheetAdapter.submitList(initialList) + Log.i("$TAG Submitted [${initialList.size}] items for default reactions list") + } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt index 59ece82ae..a39217ad3 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt @@ -6,12 +6,15 @@ import androidx.lifecycle.MutableLiveData import org.linphone.R import org.linphone.core.ChatMessage import org.linphone.core.ChatMessage.State +import org.linphone.core.ChatMessageListenerStub +import org.linphone.core.ParticipantImdnState import org.linphone.core.tools.Log import org.linphone.utils.AppUtils import org.linphone.utils.TimestampUtils class ChatMessageDeliveryModel @WorkerThread constructor( - private val chatMessage: ChatMessage + private val chatMessage: ChatMessage, + private val onDeliveryUpdated: ((model: ChatMessageDeliveryModel) -> Unit)? = null ) { companion object { private const val TAG = "[Chat Message Delivery Model]" @@ -33,10 +36,24 @@ class ChatMessageDeliveryModel @WorkerThread constructor( private val errorModels = arrayListOf() - init { - computeDeliveryStatus() + private val chatMessageListener = object : ChatMessageListenerStub() { + @WorkerThread + override fun onParticipantImdnStateChanged( + message: ChatMessage, + state: ParticipantImdnState + ) { + computeDeliveryStatus() + } + } - // TODO: add listener to update in real time the lists + init { + chatMessage.addListener(chatMessageListener) + computeDeliveryStatus() + } + + @WorkerThread + fun destroy() { + chatMessage.removeListener(chatMessageListener) } @UiThread @@ -59,6 +76,11 @@ class ChatMessageDeliveryModel @WorkerThread constructor( @WorkerThread private fun computeDeliveryStatus() { + displayedModels.clear() + deliveredModels.clear() + sentModels.clear() + errorModels.clear() + for (participant in chatMessage.getParticipantsByImdnState(State.Displayed)) { displayedModels.add( ChatMessageBottomSheetParticipantModel( @@ -136,5 +158,6 @@ class ChatMessageDeliveryModel @WorkerThread constructor( Log.i( "$TAG There are [$readCount] that have read this message, [$receivedCount] that have received it, [$sentCount] that haven't received it yet and [$errorCount] that probably won't receive it due to an error" ) + onDeliveryUpdated?.invoke(this) } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt index 0e96ba7d2..54e42cf53 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt @@ -3,11 +3,15 @@ package org.linphone.ui.main.chat.model import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.core.Address import org.linphone.core.ChatMessage +import org.linphone.core.ChatMessageListenerStub +import org.linphone.core.ChatMessageReaction import org.linphone.core.tools.Log class ChatMessageReactionsModel @WorkerThread constructor( - private val chatMessage: ChatMessage + private val chatMessage: ChatMessage, + private val onReactionsUpdated: ((model: ChatMessageReactionsModel) -> Unit)? = null ) { companion object { private const val TAG = "[Chat Message Reactions Model]" @@ -19,9 +23,28 @@ class ChatMessageReactionsModel @WorkerThread constructor( val reactionsMap = HashMap() + private val chatMessageListener = object : ChatMessageListenerStub() { + @WorkerThread + override fun onReactionRemoved(message: ChatMessage, address: Address) { + Log.i("$TAG Reaction has been removed, updating reactions list") + computeReactions() + } + + @WorkerThread + override fun onNewMessageReaction(message: ChatMessage, reaction: ChatMessageReaction) { + Log.i("$TAG A new reaction has been received, updating reactions list") + computeReactions() + } + } + init { + chatMessage.addListener(chatMessageListener) computeReactions() - // TODO: add listener to update in real time the lists + } + + @WorkerThread + fun destroy() { + chatMessage.removeListener(chatMessageListener) } fun filterReactions(emoji: String): ArrayList { @@ -75,5 +98,6 @@ class ChatMessageReactionsModel @WorkerThread constructor( "$TAG [${differentReactionsList.size}] reactions found on a total of [${allReactions.size}]" ) differentReactions.postValue(differentReactionsList) + onReactionsUpdated?.invoke(this) } }