Reworked how chat rooms list works, should speed first display but slow down a bit the scrolling

This commit is contained in:
Sylvain Berfini 2022-06-13 13:56:34 +02:00
parent 0e0fd6106c
commit c5a34363a3
9 changed files with 289 additions and 159 deletions

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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? {

View file

@ -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
}
}

View file

@ -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 {

View file

@ -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" />