From f855426a9f9be79a3fd500696cf01c0d3c1ccb42 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 23 Oct 2023 16:10:21 +0200 Subject: [PATCH] Made chat message delivery bottom sheet generic to also use it to display emoji reactions lists --- ...er.kt => ChatMessageBottomSheetAdapter.kt} | 32 +-- .../chat/adapter/ConversationEventAdapter.kt | 9 + .../chat/fragment/ConversationFragment.kt | 206 ++++++++++++------ .../fragment/MessageDeliveryDialogFragment.kt | 51 ----- ...ChatMessageBottomSheetParticipantModel.kt} | 7 +- .../chat/model/ChatMessageDeliveryModel.kt | 46 ++-- .../chat/model/ChatMessageReactionsModel.kt | 61 ++++++ .../main/res/layout/chat_bubble_incoming.xml | 4 + .../main/res/layout/chat_bubble_outgoing.xml | 4 + .../res/layout/chat_conversation_fragment.xml | 4 +- ...heet.xml => chat_message_bottom_sheet.xml} | 7 +- ...> chat_message_bottom_sheet_list_cell.xml} | 4 +- app/src/main/res/values/strings.xml | 2 + 13 files changed, 269 insertions(+), 168 deletions(-) rename app/src/main/java/org/linphone/ui/main/chat/adapter/{ChatMessageDeliveryAdapter.kt => ChatMessageBottomSheetAdapter.kt} (55%) delete mode 100644 app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt rename app/src/main/java/org/linphone/ui/main/chat/model/{ChatMessageParticipantDeliveryModel.kt => ChatMessageBottomSheetParticipantModel.kt} (65%) create mode 100644 app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt rename app/src/main/res/layout/{chat_message_delivery_bottom_sheet.xml => chat_message_bottom_sheet.xml} (91%) rename app/src/main/res/layout/{chat_message_delivery_list_cell.xml => chat_message_bottom_sheet_list_cell.xml} (96%) diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageDeliveryAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt similarity index 55% rename from app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageDeliveryAdapter.kt rename to app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt index 283bdd850..b7cd3b4ce 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageDeliveryAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ChatMessageBottomSheetAdapter.kt @@ -9,19 +9,19 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import org.linphone.R -import org.linphone.databinding.ChatMessageDeliveryListCellBinding -import org.linphone.ui.main.chat.model.ChatMessageParticipantDeliveryModel +import org.linphone.databinding.ChatMessageBottomSheetListCellBinding +import org.linphone.ui.main.chat.model.ChatMessageBottomSheetParticipantModel -class ChatMessageDeliveryAdapter( +class ChatMessageBottomSheetAdapter( private val viewLifecycleOwner: LifecycleOwner -) : ListAdapter( - ChatDeliveryDiffCallback() +) : ListAdapter( + ParticipantDiffCallback() ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val binding: ChatMessageDeliveryListCellBinding = DataBindingUtil.inflate( + val binding: ChatMessageBottomSheetListCellBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), - R.layout.chat_message_delivery_list_cell, + R.layout.chat_message_bottom_sheet_list_cell, parent, false ) @@ -33,12 +33,12 @@ class ChatMessageDeliveryAdapter( } inner class ViewHolder( - val binding: ChatMessageDeliveryListCellBinding + val binding: ChatMessageBottomSheetListCellBinding ) : RecyclerView.ViewHolder(binding.root) { @UiThread - fun bind(deliveryModel: ChatMessageParticipantDeliveryModel) { + fun bind(bottomSheetModel: ChatMessageBottomSheetParticipantModel) { with(binding) { - model = deliveryModel + model = bottomSheetModel lifecycleOwner = viewLifecycleOwner @@ -47,19 +47,19 @@ class ChatMessageDeliveryAdapter( } } - private class ChatDeliveryDiffCallback : DiffUtil.ItemCallback() { + private class ParticipantDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame( - oldItem: ChatMessageParticipantDeliveryModel, - newItem: ChatMessageParticipantDeliveryModel + oldItem: ChatMessageBottomSheetParticipantModel, + newItem: ChatMessageBottomSheetParticipantModel ): Boolean { return oldItem.sipUri == newItem.sipUri } override fun areContentsTheSame( - oldItem: ChatMessageParticipantDeliveryModel, - newItem: ChatMessageParticipantDeliveryModel + oldItem: ChatMessageBottomSheetParticipantModel, + newItem: ChatMessageBottomSheetParticipantModel ): Boolean { - return oldItem.time == newItem.time + return oldItem.value == newItem.value } } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt index 6bb8b0e21..cd10602fa 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt @@ -50,6 +50,9 @@ class ConversationEventAdapter( val showDeliveryForChatMessageModelEvent: MutableLiveData> by lazy { MutableLiveData>() } + val showReactionForChatMessageModelEvent: MutableLiveData> by lazy { + MutableLiveData>() + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { @@ -123,6 +126,9 @@ class ConversationEventAdapter( setShowDeliveryInfoClickListener { showDeliveryForChatMessageModelEvent.value = Event(message) } + setShowReactionInfoClickListener { + showReactionForChatMessageModelEvent.value = Event(message) + } lifecycleOwner = viewLifecycleOwner executePendingBindings() @@ -140,6 +146,9 @@ class ConversationEventAdapter( setShowDeliveryInfoClickListener { showDeliveryForChatMessageModelEvent.value = Event(message) } + setShowReactionInfoClickListener { + showReactionForChatMessageModelEvent.value = Event(message) + } lifecycleOwner = viewLifecycleOwner executePendingBindings() diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt index 492f24419..60406998d 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt @@ -56,10 +56,11 @@ import org.linphone.core.ChatMessage import org.linphone.core.tools.Log import org.linphone.databinding.ChatConversationFragmentBinding import org.linphone.databinding.ChatConversationLongPressMenuBinding -import org.linphone.ui.main.chat.adapter.ChatMessageDeliveryAdapter +import org.linphone.ui.main.chat.adapter.ChatMessageBottomSheetAdapter import org.linphone.ui.main.chat.adapter.ConversationEventAdapter import org.linphone.ui.main.chat.model.ChatMessageDeliveryModel import org.linphone.ui.main.chat.model.ChatMessageModel +import org.linphone.ui.main.chat.model.ChatMessageReactionsModel import org.linphone.ui.main.chat.viewmodel.ConversationViewModel import org.linphone.ui.main.fragment.GenericFragment import org.linphone.utils.AppUtils @@ -83,7 +84,7 @@ class ConversationFragment : GenericFragment() { private lateinit var adapter: ConversationEventAdapter - private lateinit var deliveryAdapter: ChatMessageDeliveryAdapter + private lateinit var bottomSheetAdapter: ChatMessageBottomSheetAdapter private val pickMedia = registerForActivityResult( ActivityResultContracts.PickMultipleVisualMedia() @@ -162,15 +163,15 @@ class ConversationFragment : GenericFragment() { binding.eventsList.setHasFixedSize(true) binding.eventsList.adapter = adapter - deliveryAdapter = ChatMessageDeliveryAdapter(viewLifecycleOwner) - binding.messageDelivery.deliveryList.setHasFixedSize(true) - binding.messageDelivery.deliveryList.adapter = deliveryAdapter - val layoutManager = LinearLayoutManager(requireContext()) binding.eventsList.layoutManager = layoutManager - val deliveryLayoutManager = LinearLayoutManager(requireContext()) - binding.messageDelivery.deliveryList.layoutManager = deliveryLayoutManager + bottomSheetAdapter = ChatMessageBottomSheetAdapter(viewLifecycleOwner) + binding.messageBottomSheet.bottomSheetList.setHasFixedSize(true) + binding.messageBottomSheet.bottomSheetList.adapter = bottomSheetAdapter + + val bottomSheetLayoutManager = LinearLayoutManager(requireContext()) + binding.messageBottomSheet.bottomSheetList.layoutManager = bottomSheetLayoutManager adapter.chatMessageLongPressEvent.observe(viewLifecycleOwner) { it.consume { model -> @@ -195,13 +196,23 @@ class ConversationFragment : GenericFragment() { adapter.showDeliveryForChatMessageModelEvent.observe(viewLifecycleOwner) { it.consume { model -> if (viewModel.isGroup.value == true) { - showDeliveryBottomSheetDialog(model) + showDeliveryBottomSheetDialog(model, showDelivery = true) } else { Log.w("$TAG Conversation is not a group, not showing delivery bottom sheet") } } } + adapter.showReactionForChatMessageModelEvent.observe(viewLifecycleOwner) { + it.consume { model -> + if (viewModel.isGroup.value == true) { + showDeliveryBottomSheetDialog(model, showReactions = true) + } else { + Log.w("$TAG Conversation is not a group, not showing reactions bottom sheet") + } + } + } + binding.setOpenFilePickerClickListener { Log.i("$TAG Opening media picker") pickMedia.launch( @@ -320,74 +331,141 @@ class ConversationFragment : GenericFragment() { } @UiThread - private fun showDeliveryBottomSheetDialog(chatMessageModel: ChatMessageModel) { - val deliveryBottomSheetBehavior = BottomSheetBehavior.from(binding.messageDelivery.root) + private fun showDeliveryBottomSheetDialog( + chatMessageModel: ChatMessageModel, + showDelivery: Boolean = false, + showReactions: Boolean = false + ) { + val deliveryBottomSheetBehavior = BottomSheetBehavior.from(binding.messageBottomSheet.root) deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED - binding.messageDelivery.setHandleClickedListener { + binding.messageBottomSheet.setHandleClickedListener { deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED } lifecycleScope.launch { withContext(Dispatchers.IO) { + // Wait for previous bottom sheet to go away delay(200) withContext(Dispatchers.Main) { - coreContext.postOnCoreThread { - val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage) - - coreContext.postOnMainThread { - model.deliveryModels.observe(viewLifecycleOwner) { - deliveryAdapter.submitList(it) - } - - binding.messageDelivery.tabs.removeAllTabs() - binding.messageDelivery.tabs.addTab( - binding.messageDelivery.tabs.newTab().setText(model.readLabel.value).setId( - ChatMessage.State.Displayed.toInt() - ) - ) - binding.messageDelivery.tabs.addTab( - binding.messageDelivery.tabs.newTab().setText( - model.receivedLabel.value - ).setId( - ChatMessage.State.DeliveredToUser.toInt() - ) - ) - binding.messageDelivery.tabs.addTab( - binding.messageDelivery.tabs.newTab().setText(model.sentLabel.value).setId( - ChatMessage.State.Delivered.toInt() - ) - ) - binding.messageDelivery.tabs.addTab( - binding.messageDelivery.tabs.newTab().setText( - model.errorLabel.value - ).setId( - ChatMessage.State.NotDelivered.toInt() - ) - ) - - binding.messageDelivery.tabs.setOnTabSelectedListener(object : OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab?) { - val state = tab?.id ?: ChatMessage.State.Displayed.toInt() - model.computeListForState(ChatMessage.State.fromInt(state)) - } - - override fun onTabUnselected(tab: TabLayout.Tab?) { - } - - override fun onTabReselected(tab: TabLayout.Tab?) { - } - }) - - binding.messageDelivery.model = model - - binding.messageDelivery.root.visibility = View.VISIBLE - deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED - } + if (showDelivery) { + prepareBottomSheetForDeliveryStatus(chatMessageModel) + } else if (showReactions) { + prepareBottomSheetForReactions(chatMessageModel) } + + binding.messageBottomSheet.root.visibility = View.VISIBLE + deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED } } } } + + @UiThread + private fun prepareBottomSheetForDeliveryStatus(chatMessageModel: ChatMessageModel) { + coreContext.postOnCoreThread { + val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage) + + coreContext.postOnMainThread { + 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 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()) + + 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 + } + + 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/fragment/MessageDeliveryDialogFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt deleted file mode 100644 index 54f2518fc..000000000 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2010-2023 Belledonne Communications SARL. - * - * This file is part of linphone-android - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.linphone.ui.main.chat.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.annotation.UiThread -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import org.linphone.databinding.ChatMessageDeliveryBottomSheetBinding -import org.linphone.ui.main.chat.model.ChatMessageDeliveryModel -import org.linphone.ui.main.chat.model.ChatMessageModel - -@UiThread -class MessageDeliveryDialogFragment( - chatMessageModel: ChatMessageModel -) : BottomSheetDialogFragment() { - companion object { - const val TAG = "MessageDeliveryDialogFragment" - } - - val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage) - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val view = ChatMessageDeliveryBottomSheetBinding.inflate(layoutInflater) - view.model = model - return view.root - } -} diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageBottomSheetParticipantModel.kt similarity index 65% rename from app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt rename to app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageBottomSheetParticipantModel.kt index 5c9583567..0ee472e54 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageBottomSheetParticipantModel.kt @@ -3,15 +3,12 @@ package org.linphone.ui.main.chat.model import androidx.annotation.WorkerThread import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.core.Address -import org.linphone.utils.TimestampUtils -class ChatMessageParticipantDeliveryModel @WorkerThread constructor( +class ChatMessageBottomSheetParticipantModel @WorkerThread constructor( address: Address, - timestamp: Long + val value: String ) { val sipUri = address.asStringUriOnly() val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address) - - val time = TimestampUtils.toString(timestamp) } 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 50ad304f4..eb571ab3d 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 @@ -8,6 +8,7 @@ import org.linphone.core.ChatMessage import org.linphone.core.ChatMessage.State import org.linphone.core.tools.Log import org.linphone.utils.AppUtils +import org.linphone.utils.TimestampUtils class ChatMessageDeliveryModel @WorkerThread constructor( private val chatMessage: ChatMessage @@ -24,34 +25,34 @@ class ChatMessageDeliveryModel @WorkerThread constructor( val errorLabel = MutableLiveData() - val deliveryModels = MutableLiveData>() + val displayedModels = arrayListOf() - private val displayedModels = arrayListOf() + private val deliveredModels = arrayListOf() - private val deliveredModels = arrayListOf() + private val sentModels = arrayListOf() - private val sentModels = arrayListOf() - - private val errorModels = arrayListOf() + private val errorModels = arrayListOf() init { computeDeliveryStatus() + + // TODO: add listener to update in real time the lists } @UiThread - fun computeListForState(state: State) { - when (state) { + fun computeListForState(state: State): ArrayList { + return when (state) { State.DeliveredToUser -> { - deliveryModels.value = deliveredModels + deliveredModels } State.Delivered -> { - deliveryModels.value = sentModels + sentModels } State.NotDelivered -> { - deliveryModels.value = errorModels + errorModels } else -> { - deliveryModels.value = displayedModels + displayedModels } } } @@ -60,17 +61,17 @@ class ChatMessageDeliveryModel @WorkerThread constructor( private fun computeDeliveryStatus() { for (participant in chatMessage.getParticipantsByImdnState(State.Displayed)) { displayedModels.add( - ChatMessageParticipantDeliveryModel( + ChatMessageBottomSheetParticipantModel( participant.participant.address, - participant.stateChangeTime + TimestampUtils.timeToString(participant.stateChangeTime) ) ) } // Always add ourselves to prevent empty list displayedModels.add( - ChatMessageParticipantDeliveryModel( + ChatMessageBottomSheetParticipantModel( chatMessage.localAddress, - chatMessage.time + TimestampUtils.timeToString(chatMessage.time) ) ) val readCount = displayedModels.size.toString() @@ -83,9 +84,9 @@ class ChatMessageDeliveryModel @WorkerThread constructor( for (participant in chatMessage.getParticipantsByImdnState(State.DeliveredToUser)) { deliveredModels.add( - ChatMessageParticipantDeliveryModel( + ChatMessageBottomSheetParticipantModel( participant.participant.address, - participant.stateChangeTime + TimestampUtils.timeToString(participant.stateChangeTime) ) ) } @@ -99,9 +100,9 @@ class ChatMessageDeliveryModel @WorkerThread constructor( for (participant in chatMessage.getParticipantsByImdnState(State.Delivered)) { sentModels.add( - ChatMessageParticipantDeliveryModel( + ChatMessageBottomSheetParticipantModel( participant.participant.address, - participant.stateChangeTime + TimestampUtils.timeToString(participant.stateChangeTime) ) ) } @@ -115,9 +116,9 @@ class ChatMessageDeliveryModel @WorkerThread constructor( for (participant in chatMessage.getParticipantsByImdnState(State.NotDelivered)) { errorModels.add( - ChatMessageParticipantDeliveryModel( + ChatMessageBottomSheetParticipantModel( participant.participant.address, - participant.stateChangeTime + TimestampUtils.timeToString(participant.stateChangeTime) ) ) } @@ -129,7 +130,6 @@ class ChatMessageDeliveryModel @WorkerThread constructor( ) ) - deliveryModels.postValue(displayedModels) Log.i("$TAG Message ID [${chatMessage.messageId}] is in state [${chatMessage.state}]") 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" 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 new file mode 100644 index 000000000..3df3caffd --- /dev/null +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageReactionsModel.kt @@ -0,0 +1,61 @@ +package org.linphone.ui.main.chat.model + +import androidx.annotation.WorkerThread +import androidx.lifecycle.MutableLiveData +import org.linphone.core.ChatMessage +import org.linphone.core.tools.Log + +class ChatMessageReactionsModel @WorkerThread constructor( + private val chatMessage: ChatMessage +) { + companion object { + private const val TAG = "[Chat Message Reactions Model]" + } + + val allReactions = arrayListOf() + + val differentReactions = MutableLiveData>() + + val reactionsMap = HashMap() + + init { + computeReactions() + // TODO: add listener to update in real time the lists + } + + fun filterReactions(emoji: String): ArrayList { + val filteredList = arrayListOf() + + for (reaction in allReactions) { + if (reaction.value == emoji) { + filteredList.add(reaction) + } + } + + return filteredList + } + + @WorkerThread + private fun computeReactions() { + reactionsMap.clear() + allReactions.clear() + + val differentReactionsList = arrayListOf() + for (reaction in chatMessage.reactions) { + val body = reaction.body + val count = reactionsMap.getOrDefault(body, 0) + reactionsMap[body] = count + 1 + + allReactions.add(ChatMessageBottomSheetParticipantModel(reaction.fromAddress, body)) + + if (!differentReactionsList.contains(body)) { + differentReactionsList.add(body) + } + } + + Log.i( + "$TAG [${differentReactionsList.size}] reactions found on a total of [${allReactions.size}]" + ) + differentReactions.postValue(differentReactionsList) + } +} diff --git a/app/src/main/res/layout/chat_bubble_incoming.xml b/app/src/main/res/layout/chat_bubble_incoming.xml index 452bc80cc..fcc13c433 100644 --- a/app/src/main/res/layout/chat_bubble_incoming.xml +++ b/app/src/main/res/layout/chat_bubble_incoming.xml @@ -13,6 +13,9 @@ + @@ -116,6 +119,7 @@ + @@ -93,6 +96,7 @@ + layout="@layout/chat_message_bottom_sheet" /> diff --git a/app/src/main/res/layout/chat_message_delivery_bottom_sheet.xml b/app/src/main/res/layout/chat_message_bottom_sheet.xml similarity index 91% rename from app/src/main/res/layout/chat_message_delivery_bottom_sheet.xml rename to app/src/main/res/layout/chat_message_bottom_sheet.xml index 17e4e2c91..f31595665 100644 --- a/app/src/main/res/layout/chat_message_delivery_bottom_sheet.xml +++ b/app/src/main/res/layout/chat_message_bottom_sheet.xml @@ -7,9 +7,6 @@ - + type="org.linphone.ui.main.chat.model.ChatMessageBottomSheetParticipantModel" /> Received (%s) Sent (%s) Error (%s) + Reactions (%s) + %s (%s) No meeting for the moment… New meeting