mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-05-03 15:26:27 +00:00
Made chat message bottom sheet content dynamic
This commit is contained in:
parent
6fec1958b8
commit
6ae57158b7
5 changed files with 191 additions and 107 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -46,14 +46,14 @@ class ChatMessageBottomSheetAdapter : ListAdapter<ChatMessageBottomSheetParticip
|
|||
oldItem: ChatMessageBottomSheetParticipantModel,
|
||||
newItem: ChatMessageBottomSheetParticipantModel
|
||||
): Boolean {
|
||||
return oldItem.sipUri == newItem.sipUri
|
||||
return oldItem.sipUri == newItem.sipUri && oldItem.value == newItem.value
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ChatMessageBottomSheetParticipantModel,
|
||||
newItem: ChatMessageBottomSheetParticipantModel
|
||||
): Boolean {
|
||||
return oldItem.value == newItem.value
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ import androidx.activity.result.PickVisualMediaRequest
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -154,6 +153,10 @@ class ConversationFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private var bottomSheetDeliveryModel: ChatMessageDeliveryModel? = null
|
||||
|
||||
private var bottomSheetReactionsModel: ChatMessageReactionsModel? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
|
@ -454,6 +457,11 @@ class ConversationFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
override fun onPause() {
|
||||
coreContext.postOnCoreThread {
|
||||
bottomSheetReactionsModel?.destroy()
|
||||
bottomSheetDeliveryModel?.destroy()
|
||||
}
|
||||
|
||||
if (viewModel.isGroup.value == true) {
|
||||
binding.sendArea.messageToSend.removeTextChangedListener(textObserver)
|
||||
}
|
||||
|
|
@ -547,11 +555,11 @@ class ConversationFragment : GenericFragment() {
|
|||
showDelivery: Boolean = false,
|
||||
showReactions: Boolean = false
|
||||
) {
|
||||
val deliveryBottomSheetBehavior = BottomSheetBehavior.from(binding.messageBottomSheet.root)
|
||||
deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
val bottomSheetBehavior = BottomSheetBehavior.from(binding.messageBottomSheet.root)
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
|
||||
binding.messageBottomSheet.setHandleClickedListener {
|
||||
deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
if (binding.messageBottomSheet.bottomSheetList.adapter != bottomSheetAdapter) {
|
||||
binding.messageBottomSheet.bottomSheetList.adapter = bottomSheetAdapter
|
||||
|
|
@ -569,7 +577,7 @@ class ConversationFragment : GenericFragment() {
|
|||
prepareBottomSheetForReactions(chatMessageModel)
|
||||
}
|
||||
|
||||
deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -578,107 +586,132 @@ class ConversationFragment : GenericFragment() {
|
|||
@UiThread
|
||||
private fun prepareBottomSheetForDeliveryStatus(chatMessageModel: ChatMessageModel) {
|
||||
coreContext.postOnCoreThread {
|
||||
val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage)
|
||||
bottomSheetDeliveryModel?.destroy()
|
||||
|
||||
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")
|
||||
val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage) { deliveryModel ->
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ChatMessageBottomSheetParticipantModel>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, Int>()
|
||||
|
||||
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<ChatMessageBottomSheetParticipantModel> {
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue