Reworked how chat rooms list works, should speed first display but slow down a bit the scrolling
This commit is contained in:
parent
0e0fd6106c
commit
c5a34363a3
9 changed files with 289 additions and 159 deletions
|
@ -28,17 +28,16 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.main.adapters.SelectionListAdapter
|
||||
import org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel
|
||||
import org.linphone.activities.main.chat.data.ChatRoomData
|
||||
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.databinding.ChatRoomListCellBinding
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
class ChatRoomsListAdapter(
|
||||
selectionVM: ListTopBarViewModel,
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : SelectionListAdapter<ChatRoomViewModel, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
||||
) : SelectionListAdapter<ChatRoom, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
||||
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
||||
MutableLiveData<Event<ChatRoom>>()
|
||||
}
|
||||
|
@ -65,9 +64,9 @@ class ChatRoomsListAdapter(
|
|||
inner class ViewHolder(
|
||||
private val binding: ChatRoomListCellBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(chatRoomViewModel: ChatRoomViewModel) {
|
||||
fun bind(chatRoom: ChatRoom) {
|
||||
with(binding) {
|
||||
viewModel = chatRoomViewModel
|
||||
data = ChatRoomData(chatRoom)
|
||||
|
||||
lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
|
@ -85,7 +84,7 @@ class ChatRoomsListAdapter(
|
|||
if (selectionViewModel.isEditionEnabled.value == true) {
|
||||
selectionViewModel.onToggleSelect(bindingAdapterPosition)
|
||||
} else {
|
||||
selectedChatRoomEvent.value = Event(chatRoomViewModel.chatRoom)
|
||||
selectedChatRoomEvent.value = Event(chatRoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,18 +103,18 @@ class ChatRoomsListAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoomViewModel>() {
|
||||
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoom>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: ChatRoomViewModel,
|
||||
newItem: ChatRoomViewModel
|
||||
oldItem: ChatRoom,
|
||||
newItem: ChatRoom
|
||||
): Boolean {
|
||||
return LinphoneUtils.areChatRoomsTheSame(oldItem.chatRoom, newItem.chatRoom)
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ChatRoomViewModel,
|
||||
newItem: ChatRoomViewModel
|
||||
oldItem: ChatRoom,
|
||||
newItem: ChatRoom
|
||||
): Boolean {
|
||||
return true
|
||||
return false // To force redraw
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.activities.main.chat.data
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.contact.ContactDataInterface
|
||||
import org.linphone.core.*
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
||||
class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
|
||||
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
|
||||
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
|
||||
override val showGroupChatAvatar: Boolean
|
||||
get() = conferenceChatRoom && !oneToOneChatRoom
|
||||
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
|
||||
|
||||
val unreadMessagesCount = MutableLiveData<Int>()
|
||||
|
||||
val subject = MutableLiveData<String>()
|
||||
|
||||
val securityLevelIcon = MutableLiveData<Int>()
|
||||
|
||||
val securityLevelContentDescription = MutableLiveData<Int>()
|
||||
|
||||
val ephemeralEnabled = MutableLiveData<Boolean>()
|
||||
|
||||
val lastUpdate = MutableLiveData<String>()
|
||||
|
||||
val lastMessageText = MutableLiveData<SpannableStringBuilder>()
|
||||
|
||||
val notificationsMuted = MutableLiveData<Boolean>()
|
||||
|
||||
private val basicChatRoom: Boolean by lazy {
|
||||
chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())
|
||||
}
|
||||
|
||||
val oneToOneChatRoom: Boolean by lazy {
|
||||
chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())
|
||||
}
|
||||
|
||||
private val conferenceChatRoom: Boolean by lazy {
|
||||
chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt())
|
||||
}
|
||||
|
||||
val encryptedChatRoom: Boolean by lazy {
|
||||
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
|
||||
}
|
||||
|
||||
init {
|
||||
unreadMessagesCount.value = chatRoom.unreadMessagesCount
|
||||
|
||||
subject.value = chatRoom.subject
|
||||
updateSecurityIcon()
|
||||
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
|
||||
|
||||
contactLookup()
|
||||
formatLastMessage(chatRoom.lastMessageInHistory)
|
||||
|
||||
notificationsMuted.value = areNotificationsMuted()
|
||||
}
|
||||
|
||||
private fun updateSecurityIcon() {
|
||||
val level = chatRoom.securityLevel
|
||||
securityLevel.value = level
|
||||
|
||||
securityLevelIcon.value = when (level) {
|
||||
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator
|
||||
ChatRoomSecurityLevel.Encrypted -> R.drawable.security_1_indicator
|
||||
else -> R.drawable.security_alert_indicator
|
||||
}
|
||||
securityLevelContentDescription.value = when (level) {
|
||||
ChatRoomSecurityLevel.Safe -> R.string.content_description_security_level_safe
|
||||
ChatRoomSecurityLevel.Encrypted -> R.string.content_description_security_level_encrypted
|
||||
else -> R.string.content_description_security_level_unsafe
|
||||
}
|
||||
}
|
||||
|
||||
private fun contactLookup() {
|
||||
displayName.value = when {
|
||||
basicChatRoom -> LinphoneUtils.getDisplayName(
|
||||
chatRoom.peerAddress
|
||||
)
|
||||
oneToOneChatRoom -> LinphoneUtils.getDisplayName(
|
||||
chatRoom.participants.firstOrNull()?.address ?: chatRoom.peerAddress
|
||||
)
|
||||
conferenceChatRoom -> chatRoom.subject.orEmpty()
|
||||
else -> chatRoom.peerAddress.asStringUriOnly()
|
||||
}
|
||||
|
||||
if (oneToOneChatRoom) {
|
||||
searchMatchingContact()
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchMatchingContact() {
|
||||
val remoteAddress = if (basicChatRoom) {
|
||||
chatRoom.peerAddress
|
||||
} else {
|
||||
if (chatRoom.participants.isNotEmpty()) {
|
||||
chatRoom.participants[0].address
|
||||
} else {
|
||||
Log.e("[Chat Room] $chatRoom doesn't have any participant in state ${chatRoom.state}!")
|
||||
null
|
||||
}
|
||||
}
|
||||
if (remoteAddress != null) {
|
||||
contact.value = coreContext.contactsManager.findContactByAddress(remoteAddress)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatLastMessage(msg: ChatMessage?) {
|
||||
val lastUpdateTime = chatRoom.lastUpdateTime
|
||||
coroutineScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
lastUpdate.postValue(TimestampUtils.toString(lastUpdateTime, true))
|
||||
}
|
||||
}
|
||||
|
||||
val builder = SpannableStringBuilder()
|
||||
if (msg == null) {
|
||||
lastMessageText.value = builder
|
||||
return
|
||||
}
|
||||
|
||||
val sender: String =
|
||||
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
|
||||
?: LinphoneUtils.getDisplayName(msg.fromAddress)
|
||||
builder.append(sender)
|
||||
builder.append(": ")
|
||||
|
||||
for (content in msg.contents) {
|
||||
if (content.isIcalendar) {
|
||||
val body = AppUtils.getString(R.string.conference_invitation)
|
||||
builder.append(body)
|
||||
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
|
||||
} else if (content.isFile || content.isFileTransfer) {
|
||||
builder.append(content.name + " ")
|
||||
} else if (content.isText) {
|
||||
builder.append(content.utf8Text + " ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.trim()
|
||||
lastMessageText.value = builder
|
||||
}
|
||||
|
||||
private fun areNotificationsMuted(): Boolean {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
return corePreferences.chatRoomMuted(id)
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
package org.linphone.activities.main.chat.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
|
@ -175,16 +174,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
|
||||
isSecure = chatRoom.currentParams.isEncryptionEnabled
|
||||
|
||||
val chatRoomsListViewModel: ChatRoomsListViewModel = requireActivity().run {
|
||||
ViewModelProvider(this)[ChatRoomsListViewModel::class.java]
|
||||
}
|
||||
val chatRoomViewModel = chatRoomsListViewModel.chatRooms.value.orEmpty().find {
|
||||
it.chatRoom == chatRoom
|
||||
}
|
||||
if (chatRoomViewModel == null) {
|
||||
Log.w("[Chat Room] Couldn't find existing view model, will create a new one!")
|
||||
}
|
||||
viewModel = chatRoomViewModel ?: ViewModelProvider(
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
ChatRoomViewModelFactory(chatRoom)
|
||||
)[ChatRoomViewModel::class.java]
|
||||
|
@ -360,7 +350,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
) {
|
||||
it.consume { chatMessage ->
|
||||
listViewModel.deleteMessage(chatMessage)
|
||||
viewModel.updateLastMessageToDisplay()
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -677,7 +667,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
list.add(eventLog)
|
||||
}
|
||||
listViewModel.deleteEventLogs(list)
|
||||
viewModel.updateLastMessageToDisplay()
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
|
@ -934,10 +924,12 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
}
|
||||
popupView.setMuteListener {
|
||||
viewModel.muteNotifications(true)
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
popupView.setUnmuteListener {
|
||||
viewModel.muteNotifications(false)
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
popupView.setAddToContactsListener {
|
||||
|
@ -984,7 +976,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
Log.i("[Chat Room] Deleting event $eventLog at position $position")
|
||||
listViewModel.deleteEventLogs(arrayListOf(eventLog))
|
||||
}
|
||||
viewModel.updateLastMessageToDisplay()
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.linphone.activities.main.fragments.SecureFragment
|
|||
import org.linphone.activities.main.viewmodels.SharedMainViewModel
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.ChatRoomEphemeralFragmentBinding
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
|
||||
private lateinit var viewModel: EphemeralViewModel
|
||||
|
@ -68,6 +69,7 @@ class EphemeralFragment : SecureFragment<ChatRoomEphemeralFragmentBinding>() {
|
|||
|
||||
binding.setValidClickListener {
|
||||
viewModel.updateChatRoomEphemeralDuration()
|
||||
sharedViewModel.refreshChatRoomInListEvent.value = Event(true)
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.layoutChangedEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
|
@ -145,6 +146,18 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.refreshChatRoomInListEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
it.consume {
|
||||
val chatRoom = sharedViewModel.selectedChatRoom.value
|
||||
if (chatRoom != null) {
|
||||
listViewModel.notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
|
||||
|
||||
/* End of shared view model & sliding pane related */
|
||||
|
@ -175,8 +188,8 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
)
|
||||
val swipeListener = object : RecyclerViewSwipeListener {
|
||||
override fun onLeftToRightSwipe(viewHolder: RecyclerView.ViewHolder) {
|
||||
val chatRoomViewModel = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||
chatRoomViewModel.chatRoom.markAsRead()
|
||||
val chatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||
chatRoom.markAsRead()
|
||||
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
||||
}
|
||||
|
||||
|
@ -191,7 +204,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
|
||||
viewModel.showDeleteButton(
|
||||
{
|
||||
val deletedChatRoom = adapter.currentList[viewHolder.bindingAdapterPosition].chatRoom
|
||||
val deletedChatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||
listViewModel.deleteChatRoom(deletedChatRoom)
|
||||
if (!binding.slidingPane.isSlideable &&
|
||||
deletedChatRoom == sharedViewModel.selectedChatRoom.value
|
||||
|
@ -219,6 +232,14 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
adapter.submitList(chatRooms)
|
||||
}
|
||||
|
||||
listViewModel.chatRoomIndexUpdatedEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
it.consume { index ->
|
||||
adapter.notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
|
||||
adapter.selectedChatRoomEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
|
@ -349,10 +370,10 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
val list = ArrayList<ChatRoom>()
|
||||
var closeSlidingPane = false
|
||||
for (index in indexesOfItemToDelete) {
|
||||
val chatRoomViewModel = adapter.currentList[index]
|
||||
list.add(chatRoomViewModel.chatRoom)
|
||||
val chatRoom = adapter.currentList[index]
|
||||
list.add(chatRoom)
|
||||
|
||||
if (chatRoomViewModel.chatRoom == sharedViewModel.selectedChatRoom.value) {
|
||||
if (chatRoom == sharedViewModel.selectedChatRoom.value) {
|
||||
closeSlidingPane = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
package org.linphone.activities.main.chat.viewmodels
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.Typeface
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.animation.LinearInterpolator
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -38,7 +35,6 @@ import org.linphone.core.*
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
||||
class ChatRoomViewModelFactory(private val chatRoom: ChatRoom) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
|
@ -63,10 +59,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
|
||||
val unreadMessagesCount = MutableLiveData<Int>()
|
||||
|
||||
val lastUpdate = MutableLiveData<String>()
|
||||
|
||||
val lastMessageText = MutableLiveData<SpannableStringBuilder>()
|
||||
|
||||
val remoteIsComposing = MutableLiveData<Boolean>()
|
||||
|
||||
val composingList = MutableLiveData<String>()
|
||||
|
@ -114,8 +106,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
val groupCallAvailable: Boolean
|
||||
get() = LinphoneUtils.isRemoteConferencingAvailable()
|
||||
|
||||
val notificationsMuted = MutableLiveData<Boolean>()
|
||||
|
||||
private var addressToCall: Address? = null
|
||||
|
||||
private val bounceAnimator: ValueAnimator by lazy {
|
||||
|
@ -135,7 +125,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
override fun onContactsUpdated() {
|
||||
Log.d("[Chat Room] Contacts have changed")
|
||||
contactLookup()
|
||||
updateLastMessageToDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,11 +152,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
|
||||
override fun onChatMessageReceived(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
updateUnreadMessageCount()
|
||||
formatLastMessage(eventLog.chatMessage)
|
||||
}
|
||||
|
||||
override fun onChatMessageSent(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
formatLastMessage(eventLog.chatMessage)
|
||||
}
|
||||
|
||||
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
|
@ -210,11 +194,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
updateParticipants()
|
||||
}
|
||||
|
||||
override fun onEphemeralMessageDeleted(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
Log.i("[Chat Room] Ephemeral message deleted, updated last message displayed")
|
||||
updateLastMessageToDisplay()
|
||||
}
|
||||
|
||||
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
|
||||
ephemeralEnabled.value = chatRoom.isEphemeralEnabled
|
||||
}
|
||||
|
@ -238,23 +217,16 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
|
||||
contactLookup()
|
||||
updateParticipants()
|
||||
updateLastMessageToDisplay()
|
||||
|
||||
updateRemotesComposing()
|
||||
|
||||
notificationsMuted.value = areNotificationsMuted()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
destroy()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||
chatRoom.removeListener(chatRoomListener)
|
||||
chatRoom.core.removeListener(coreListener)
|
||||
if (corePreferences.enableAnimations) bounceAnimator.end()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
fun contactLookup() {
|
||||
|
@ -306,10 +278,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
conferenceScheduler.info = conferenceInfo
|
||||
}
|
||||
|
||||
fun updateLastMessageToDisplay() {
|
||||
formatLastMessage(chatRoom.lastMessageInHistory)
|
||||
}
|
||||
|
||||
fun areNotificationsMuted(): Boolean {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
return corePreferences.chatRoomMuted(id)
|
||||
|
@ -318,43 +286,6 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
|
|||
fun muteNotifications(mute: Boolean) {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
corePreferences.muteChatRoom(id, mute)
|
||||
notificationsMuted.value = mute
|
||||
}
|
||||
|
||||
private fun formatLastMessage(msg: ChatMessage?) {
|
||||
val lastUpdateTime = chatRoom.lastUpdateTime
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
lastUpdate.postValue(TimestampUtils.toString(lastUpdateTime, true))
|
||||
}
|
||||
}
|
||||
|
||||
val builder = SpannableStringBuilder()
|
||||
if (msg == null) {
|
||||
lastMessageText.value = builder
|
||||
return
|
||||
}
|
||||
|
||||
val sender: String =
|
||||
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
|
||||
?: LinphoneUtils.getDisplayName(msg.fromAddress)
|
||||
builder.append(sender)
|
||||
builder.append(": ")
|
||||
|
||||
for (content in msg.contents) {
|
||||
if (content.isIcalendar) {
|
||||
val body = AppUtils.getString(R.string.conference_invitation)
|
||||
builder.append(body)
|
||||
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
|
||||
} else if (content.isFile || content.isFileTransfer) {
|
||||
builder.append(content.name + " ")
|
||||
} else if (content.isText) {
|
||||
builder.append(content.utf8Text + " ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.trim()
|
||||
lastMessageText.value = builder
|
||||
}
|
||||
|
||||
fun getRemoteAddress(): Address? {
|
||||
|
|
|
@ -24,13 +24,14 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
|||
import org.linphone.R
|
||||
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
|
||||
import org.linphone.compatibility.Compatibility
|
||||
import org.linphone.contact.ContactsUpdatedListenerStub
|
||||
import org.linphone.core.*
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||
val chatRooms = MutableLiveData<ArrayList<ChatRoomViewModel>>()
|
||||
val chatRooms = MutableLiveData<ArrayList<ChatRoom>>()
|
||||
|
||||
val fileSharingPending = MutableLiveData<Boolean>()
|
||||
|
||||
|
@ -40,12 +41,14 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
|||
|
||||
val groupChatAvailable: Boolean = LinphoneUtils.isGroupChatAvailable()
|
||||
|
||||
val chatRoomIndexUpdatedEvent: MutableLiveData<Event<Int>> by lazy {
|
||||
MutableLiveData<Event<Int>>()
|
||||
}
|
||||
|
||||
private val listener: CoreListenerStub = object : CoreListenerStub() {
|
||||
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
|
||||
if (state == ChatRoom.State.Created) {
|
||||
if (chatRoom in core.chatRooms) { // Don't add empty chat room if 1-1 depending on policy
|
||||
addChatRoom(chatRoom)
|
||||
}
|
||||
updateChatRooms()
|
||||
} else if (state == ChatRoom.State.TerminationFailed) {
|
||||
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
|
||||
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
|
||||
|
@ -54,46 +57,57 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
|||
|
||||
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||
when (findChatRoomIndex(chatRoom)) {
|
||||
-1 -> addChatRoom(chatRoom)
|
||||
0 -> {}
|
||||
-1 -> updateChatRooms()
|
||||
0 -> chatRoomIndexUpdatedEvent.value = Event(0)
|
||||
else -> reorderChatRooms()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessageReceived(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||
when (findChatRoomIndex(chatRoom)) {
|
||||
-1 -> addChatRoom(chatRoom)
|
||||
0 -> {}
|
||||
-1 -> updateChatRooms()
|
||||
0 -> chatRoomIndexUpdatedEvent.value = Event(0)
|
||||
else -> reorderChatRooms()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
|
||||
override fun onChatRoomEphemeralMessageDeleted(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
|
||||
override fun onChatRoomSubjectChanged(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
}
|
||||
|
||||
private val chatRoomListener = object : ChatRoomListenerStub() {
|
||||
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
|
||||
if (newState == ChatRoom.State.Deleted) {
|
||||
val list = arrayListOf<ChatRoomViewModel>()
|
||||
for (chatRoomViewModel in chatRooms.value.orEmpty()) {
|
||||
if (chatRoomViewModel.chatRoom != chatRoom) {
|
||||
list.add(chatRoomViewModel)
|
||||
} else {
|
||||
chatRoomViewModel.destroy()
|
||||
}
|
||||
}
|
||||
chatRooms.value = list
|
||||
updateChatRooms()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val contactsListener = object : ContactsUpdatedListenerStub() {
|
||||
override fun onContactUpdated(friend: Friend) {
|
||||
updateChatRooms()
|
||||
}
|
||||
}
|
||||
|
||||
private var chatRoomsToDeleteCount = 0
|
||||
|
||||
init {
|
||||
updateChatRooms()
|
||||
coreContext.core.addListener(listener)
|
||||
coreContext.contactsManager.addListener(contactsListener)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
chatRooms.value.orEmpty().forEach(ChatRoomViewModel::destroy)
|
||||
coreContext.contactsManager.removeListener(contactsListener)
|
||||
coreContext.core.removeListener(listener)
|
||||
|
||||
super.onCleared()
|
||||
|
@ -128,42 +142,27 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
|||
}
|
||||
|
||||
fun updateChatRooms() {
|
||||
chatRooms.value.orEmpty().forEach(ChatRoomViewModel::destroy)
|
||||
|
||||
val list = arrayListOf<ChatRoomViewModel>()
|
||||
for (chatRoom in coreContext.core.chatRooms) {
|
||||
val viewModel = ChatRoomViewModel(chatRoom)
|
||||
list.add(viewModel)
|
||||
}
|
||||
val list = arrayListOf<ChatRoom>()
|
||||
list.addAll(coreContext.core.chatRooms)
|
||||
chatRooms.value = list
|
||||
}
|
||||
|
||||
private fun addChatRoom(chatRoom: ChatRoom) {
|
||||
val exists = chatRooms.value.orEmpty().find {
|
||||
it.chatRoom.localAddress.weakEqual(chatRoom.localAddress) && it.chatRoom.peerAddress.weakEqual(chatRoom.peerAddress)
|
||||
}
|
||||
if (exists != null) {
|
||||
Log.w("[Chat Rooms] Do not add chat room to list, it's already here")
|
||||
return
|
||||
}
|
||||
|
||||
val list = arrayListOf<ChatRoomViewModel>()
|
||||
val viewModel = ChatRoomViewModel(chatRoom)
|
||||
list.add(viewModel)
|
||||
list.addAll(chatRooms.value.orEmpty())
|
||||
chatRooms.value = list
|
||||
fun notifyChatRoomUpdate(chatRoom: ChatRoom) {
|
||||
val index = findChatRoomIndex(chatRoom)
|
||||
if (index == -1) updateChatRooms()
|
||||
else chatRoomIndexUpdatedEvent.value = Event(index)
|
||||
}
|
||||
|
||||
private fun reorderChatRooms() {
|
||||
val list = arrayListOf<ChatRoomViewModel>()
|
||||
val list = arrayListOf<ChatRoom>()
|
||||
list.addAll(chatRooms.value.orEmpty())
|
||||
list.sortByDescending { chatRoomViewModel -> chatRoomViewModel.chatRoom.lastUpdateTime }
|
||||
list.sortByDescending { chatRoom -> chatRoom.lastUpdateTime }
|
||||
chatRooms.value = list
|
||||
}
|
||||
|
||||
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
||||
for ((index, chatRoomViewModel) in chatRooms.value.orEmpty().withIndex()) {
|
||||
if (chatRoomViewModel.chatRoom == chatRoom) {
|
||||
for ((index, cr) in chatRooms.value.orEmpty().withIndex()) {
|
||||
if (LinphoneUtils.areChatRoomsTheSame(cr, chatRoom)) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ class SharedMainViewModel : ViewModel() {
|
|||
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
|
||||
val richContentUri = MutableLiveData<Event<Uri>>()
|
||||
|
||||
val refreshChatRoomInListEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
/* Contacts */
|
||||
|
||||
val contactFragmentOpenedEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
name="forwardPending"
|
||||
type="Boolean" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.activities.main.chat.viewmodels.ChatRoomViewModel" />
|
||||
name="data"
|
||||
type="org.linphone.activities.main.chat.data.ChatRoomData" />
|
||||
<variable
|
||||
name="selectionListViewModel"
|
||||
type="org.linphone.activities.main.viewmodels.ListTopBarViewModel" />
|
||||
|
@ -38,7 +38,7 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
coilContact="@{viewModel}"
|
||||
coilContact="@{data}"
|
||||
android:layout_width="@dimen/contact_avatar_size"
|
||||
android:layout_height="@dimen/contact_avatar_size"
|
||||
android:layout_marginLeft="5dp"
|
||||
|
@ -54,9 +54,9 @@
|
|||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginLeft="25dp"
|
||||
android:contentDescription="@{viewModel.securityLevelContentDescription}"
|
||||
android:src="@{viewModel.securityLevelIcon, default=@drawable/security_alert_indicator}"
|
||||
android:visibility="@{viewModel.encryptedChatRoom ? View.VISIBLE : View.INVISIBLE, default=invisible}"
|
||||
android:contentDescription="@{data.securityLevelContentDescription}"
|
||||
android:src="@{data.securityLevelIcon, default=@drawable/security_alert_indicator}"
|
||||
android:visibility="@{data.encryptedChatRoom ? View.VISIBLE : View.INVISIBLE, default=invisible}"
|
||||
app:layout_constraintStart_toStartOf="@id/avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar" />
|
||||
|
||||
|
@ -67,7 +67,7 @@
|
|||
android:layout_marginTop="5dp"
|
||||
android:paddingRight="10dp"
|
||||
android:singleLine="true"
|
||||
android:text="@{viewModel.lastUpdate, default=`12:03`}"
|
||||
android:text="@{data.lastUpdate, default=`12:03`}"
|
||||
android:textColor="?attr/accentColor"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
@ -84,7 +84,7 @@
|
|||
android:layout_marginRight="5dp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@{viewModel.oneToOneChatRoom ? (viewModel.contact.name ?? viewModel.displayName) : viewModel.subject, default=`Lorem Ipsum`}"
|
||||
android:text="@{data.oneToOneChatRoom ? (data.contact.name ?? data.displayName) : data.subject, default=`Lorem Ipsum`}"
|
||||
app:layout_constraintStart_toEndOf="@id/date"
|
||||
app:layout_constraintEnd_toStartOf="@id/unread"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar" />
|
||||
|
@ -98,7 +98,7 @@
|
|||
android:layout_marginRight="5dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@{viewModel.lastMessageText, default=`Lorem ipsum dolor sit amet`}"
|
||||
android:text="@{data.lastMessageText, default=`Lorem ipsum dolor sit amet`}"
|
||||
app:layout_constraintStart_toEndOf="@id/date"
|
||||
app:layout_constraintEnd_toStartOf="@id/endBarrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
@ -121,7 +121,7 @@
|
|||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/content_description_muted_chat_room"
|
||||
android:src="@drawable/chat_room_menu_mute_notifications"
|
||||
android:visibility="@{viewModel.notificationsMuted ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:visibility="@{data.notificationsMuted ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintEnd_toStartOf="@id/endBarrier3"
|
||||
app:layout_constraintTop_toTopOf="@id/title" />
|
||||
|
||||
|
@ -135,8 +135,8 @@
|
|||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:text="@{String.valueOf(viewModel.unreadMessagesCount)}"
|
||||
android:visibility="@{viewModel.unreadMessagesCount == 0 ? View.GONE : View.VISIBLE, default=gone}"
|
||||
android:text="@{String.valueOf(data.unreadMessagesCount)}"
|
||||
android:visibility="@{data.unreadMessagesCount == 0 ? View.GONE : View.VISIBLE, default=gone}"
|
||||
app:layout_constraintEnd_toStartOf="@id/endBarrier2"
|
||||
app:layout_constraintTop_toTopOf="@id/title" />
|
||||
|
||||
|
@ -149,7 +149,7 @@
|
|||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/content_description_ephemeral_chat_room"
|
||||
android:src="@drawable/ephemeral_messages"
|
||||
android:visibility="@{viewModel.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:visibility="@{data.ephemeralEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/forward" />
|
||||
|
||||
|
|
Loading…
Reference in a new issue