Proper fix for chat rooms blinking
This commit is contained in:
parent
8338ea8814
commit
31e30e6214
5 changed files with 81 additions and 55 deletions
|
@ -33,12 +33,11 @@ import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
||||||
import org.linphone.core.ChatRoom
|
import org.linphone.core.ChatRoom
|
||||||
import org.linphone.databinding.ChatRoomListCellBinding
|
import org.linphone.databinding.ChatRoomListCellBinding
|
||||||
import org.linphone.utils.Event
|
import org.linphone.utils.Event
|
||||||
import org.linphone.utils.LinphoneUtils
|
|
||||||
|
|
||||||
class ChatRoomsListAdapter(
|
class ChatRoomsListAdapter(
|
||||||
selectionVM: ListTopBarViewModel,
|
selectionVM: ListTopBarViewModel,
|
||||||
private val viewLifecycleOwner: LifecycleOwner
|
private val viewLifecycleOwner: LifecycleOwner
|
||||||
) : SelectionListAdapter<ChatRoom, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
) : SelectionListAdapter<ChatRoomData, RecyclerView.ViewHolder>(selectionVM, ChatRoomDiffCallback()) {
|
||||||
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
val selectedChatRoomEvent: MutableLiveData<Event<ChatRoom>> by lazy {
|
||||||
MutableLiveData<Event<ChatRoom>>()
|
MutableLiveData<Event<ChatRoom>>()
|
||||||
}
|
}
|
||||||
|
@ -57,11 +56,6 @@ class ChatRoomsListAdapter(
|
||||||
(holder as ViewHolder).bind(getItem(position))
|
(holder as ViewHolder).bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
val room = getItem(position)
|
|
||||||
return LinphoneUtils.getChatRoomId(room).hashCode().toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun forwardPending(pending: Boolean) {
|
fun forwardPending(pending: Boolean) {
|
||||||
isForwardPending = pending
|
isForwardPending = pending
|
||||||
notifyItemRangeChanged(0, itemCount)
|
notifyItemRangeChanged(0, itemCount)
|
||||||
|
@ -70,9 +64,10 @@ class ChatRoomsListAdapter(
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
private val binding: ChatRoomListCellBinding
|
private val binding: ChatRoomListCellBinding
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
fun bind(chatRoom: ChatRoom) {
|
fun bind(chatRoomData: ChatRoomData) {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
data = ChatRoomData(chatRoom)
|
chatRoomData.update()
|
||||||
|
data = chatRoomData
|
||||||
|
|
||||||
lifecycleOwner = viewLifecycleOwner
|
lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
|
@ -90,7 +85,7 @@ class ChatRoomsListAdapter(
|
||||||
if (selectionViewModel.isEditionEnabled.value == true) {
|
if (selectionViewModel.isEditionEnabled.value == true) {
|
||||||
selectionViewModel.onToggleSelect(bindingAdapterPosition)
|
selectionViewModel.onToggleSelect(bindingAdapterPosition)
|
||||||
} else {
|
} else {
|
||||||
selectedChatRoomEvent.value = Event(chatRoom)
|
selectedChatRoomEvent.value = Event(chatRoomData.chatRoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,17 +104,17 @@ class ChatRoomsListAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoom>() {
|
private class ChatRoomDiffCallback : DiffUtil.ItemCallback<ChatRoomData>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
oldItem: ChatRoom,
|
oldItem: ChatRoomData,
|
||||||
newItem: ChatRoom
|
newItem: ChatRoomData
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return oldItem == newItem
|
return oldItem.id == newItem.id
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
override fun areContentsTheSame(
|
||||||
oldItem: ChatRoom,
|
oldItem: ChatRoomData,
|
||||||
newItem: ChatRoom
|
newItem: ChatRoomData
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return false // To force redraw
|
return false // To force redraw
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,14 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.contact.ContactDataInterface
|
import org.linphone.contact.ContactDataInterface
|
||||||
|
import org.linphone.contact.ContactsUpdatedListenerStub
|
||||||
import org.linphone.core.*
|
import org.linphone.core.*
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.utils.AppUtils
|
import org.linphone.utils.AppUtils
|
||||||
import org.linphone.utils.LinphoneUtils
|
import org.linphone.utils.LinphoneUtils
|
||||||
import org.linphone.utils.TimestampUtils
|
import org.linphone.utils.TimestampUtils
|
||||||
|
|
||||||
class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
class ChatRoomData(val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
|
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
|
||||||
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
|
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
|
||||||
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
|
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
|
||||||
|
@ -46,6 +47,8 @@ class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
override val presenceStatus: MutableLiveData<ConsolidatedPresence> = MutableLiveData<ConsolidatedPresence>()
|
override val presenceStatus: MutableLiveData<ConsolidatedPresence> = MutableLiveData<ConsolidatedPresence>()
|
||||||
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
|
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
|
||||||
|
|
||||||
|
val id = LinphoneUtils.getChatRoomId(chatRoom)
|
||||||
|
|
||||||
val unreadMessagesCount = MutableLiveData<Int>()
|
val unreadMessagesCount = MutableLiveData<Int>()
|
||||||
|
|
||||||
val subject = MutableLiveData<String>()
|
val subject = MutableLiveData<String>()
|
||||||
|
@ -82,7 +85,26 @@ class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
|
chatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val contactsListener = object : ContactsUpdatedListenerStub() {
|
||||||
|
override fun onContactsUpdated() {
|
||||||
|
if (oneToOneChatRoom && contact.value == null) {
|
||||||
|
searchMatchingContact()
|
||||||
|
if (contact.value != null) {
|
||||||
|
formatLastMessage(chatRoom.lastMessageInHistory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
coreContext.contactsManager.addListener(contactsListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
coreContext.contactsManager.removeListener(contactsListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update() {
|
||||||
unreadMessagesCount.value = chatRoom.unreadMessagesCount
|
unreadMessagesCount.value = chatRoom.unreadMessagesCount
|
||||||
presenceStatus.value = ConsolidatedPresence.Offline
|
presenceStatus.value = ConsolidatedPresence.Offline
|
||||||
|
|
||||||
|
@ -96,6 +118,11 @@ class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
notificationsMuted.value = areNotificationsMuted()
|
notificationsMuted.value = areNotificationsMuted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsRead() {
|
||||||
|
chatRoom.markAsRead()
|
||||||
|
unreadMessagesCount.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateSecurityIcon() {
|
private fun updateSecurityIcon() {
|
||||||
val level = chatRoom.securityLevel
|
val level = chatRoom.securityLevel
|
||||||
securityLevel.value = level
|
securityLevel.value = level
|
||||||
|
@ -133,8 +160,9 @@ class ChatRoomData(private val chatRoom: ChatRoom) : ContactDataInterface {
|
||||||
val remoteAddress = if (basicChatRoom) {
|
val remoteAddress = if (basicChatRoom) {
|
||||||
chatRoom.peerAddress
|
chatRoom.peerAddress
|
||||||
} else {
|
} else {
|
||||||
if (chatRoom.participants.isNotEmpty()) {
|
val participants = chatRoom.participants
|
||||||
chatRoom.participants[0].address
|
if (participants.isNotEmpty()) {
|
||||||
|
participants.first().address
|
||||||
} else {
|
} else {
|
||||||
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
|
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
|
||||||
null
|
null
|
||||||
|
|
|
@ -156,7 +156,6 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
_adapter = ChatRoomsListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
_adapter = ChatRoomsListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
||||||
// SubmitList is done on a background thread
|
// SubmitList is done on a background thread
|
||||||
// We need this adapter data observer to know when to scroll
|
// We need this adapter data observer to know when to scroll
|
||||||
adapter.setHasStableIds(true)
|
|
||||||
adapter.registerAdapterDataObserver(observer)
|
adapter.registerAdapterDataObserver(observer)
|
||||||
|
|
||||||
binding.chatList.setHasFixedSize(true)
|
binding.chatList.setHasFixedSize(true)
|
||||||
|
@ -185,9 +184,8 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
if (index < 0 || index >= adapter.currentList.size) {
|
if (index < 0 || index >= adapter.currentList.size) {
|
||||||
Log.e("[Chat] Index is out of bound, can't mark chat room as read")
|
Log.e("[Chat] Index is out of bound, can't mark chat room as read")
|
||||||
} else {
|
} else {
|
||||||
val chatRoom = adapter.currentList[viewHolder.bindingAdapterPosition]
|
val data = adapter.currentList[viewHolder.bindingAdapterPosition]
|
||||||
chatRoom.markAsRead()
|
data.markAsRead()
|
||||||
adapter.notifyItemChanged(viewHolder.bindingAdapterPosition)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +207,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
viewModel.showDeleteButton(
|
viewModel.showDeleteButton(
|
||||||
{
|
{
|
||||||
val deletedChatRoom =
|
val deletedChatRoom =
|
||||||
adapter.currentList[index]
|
adapter.currentList[index].chatRoom
|
||||||
listViewModel.deleteChatRoom(deletedChatRoom)
|
listViewModel.deleteChatRoom(deletedChatRoom)
|
||||||
if (!binding.slidingPane.isSlideable &&
|
if (!binding.slidingPane.isSlideable &&
|
||||||
deletedChatRoom == sharedViewModel.selectedChatRoom.value
|
deletedChatRoom == sharedViewModel.selectedChatRoom.value
|
||||||
|
@ -382,7 +380,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
||||||
val list = ArrayList<ChatRoom>()
|
val list = ArrayList<ChatRoom>()
|
||||||
var closeSlidingPane = false
|
var closeSlidingPane = false
|
||||||
for (index in indexesOfItemToDelete) {
|
for (index in indexesOfItemToDelete) {
|
||||||
val chatRoom = adapter.currentList[index]
|
val chatRoom = adapter.currentList[index].chatRoom
|
||||||
list.add(chatRoom)
|
list.add(chatRoom)
|
||||||
|
|
||||||
if (chatRoom == sharedViewModel.selectedChatRoom.value) {
|
if (chatRoom == sharedViewModel.selectedChatRoom.value) {
|
||||||
|
|
|
@ -22,16 +22,16 @@ package org.linphone.activities.main.chat.viewmodels
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
|
import org.linphone.activities.main.chat.data.ChatRoomData
|
||||||
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
|
import org.linphone.activities.main.viewmodels.MessageNotifierViewModel
|
||||||
import org.linphone.compatibility.Compatibility
|
import org.linphone.compatibility.Compatibility
|
||||||
import org.linphone.contact.ContactsUpdatedListenerStub
|
|
||||||
import org.linphone.core.*
|
import org.linphone.core.*
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.utils.Event
|
import org.linphone.utils.Event
|
||||||
import org.linphone.utils.LinphoneUtils
|
import org.linphone.utils.LinphoneUtils
|
||||||
|
|
||||||
class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
val chatRooms = MutableLiveData<ArrayList<ChatRoom>>()
|
val chatRooms = MutableLiveData<ArrayList<ChatRoomData>>()
|
||||||
|
|
||||||
val fileSharingPending = MutableLiveData<Boolean>()
|
val fileSharingPending = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
@ -45,10 +45,31 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
MutableLiveData<Event<Int>>()
|
MutableLiveData<Event<Int>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val chatRoomListener = object : ChatRoomListenerStub() {
|
||||||
|
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
|
||||||
|
if (newState == ChatRoom.State.Deleted) {
|
||||||
|
Log.i("[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Deleted state, removing it from list")
|
||||||
|
val list = arrayListOf<ChatRoomData>()
|
||||||
|
val id = LinphoneUtils.getChatRoomId(chatRoom)
|
||||||
|
for (data in chatRooms.value.orEmpty()) {
|
||||||
|
if (data.id != id) {
|
||||||
|
list.add(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chatRooms.value = list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val listener: CoreListenerStub = object : CoreListenerStub() {
|
private val listener: CoreListenerStub = object : CoreListenerStub() {
|
||||||
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
|
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
|
||||||
if (state == ChatRoom.State.Created) {
|
if (state == ChatRoom.State.Created) {
|
||||||
updateChatRooms()
|
Log.i("[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Created state, adding it to list")
|
||||||
|
val data = ChatRoomData(chatRoom)
|
||||||
|
val list = arrayListOf<ChatRoomData>()
|
||||||
|
list.add(data)
|
||||||
|
list.addAll(chatRooms.value.orEmpty())
|
||||||
|
chatRooms.value = list
|
||||||
} else if (state == ChatRoom.State.TerminationFailed) {
|
} else if (state == ChatRoom.State.TerminationFailed) {
|
||||||
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
|
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)
|
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
|
||||||
|
@ -80,31 +101,15 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val chatRoomListener = object : ChatRoomListenerStub() {
|
|
||||||
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
|
|
||||||
if (newState == ChatRoom.State.Deleted) {
|
|
||||||
updateChatRooms()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val contactsListener = object : ContactsUpdatedListenerStub() {
|
|
||||||
override fun onContactsUpdated() {
|
|
||||||
updateChatRooms()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var chatRoomsToDeleteCount = 0
|
private var chatRoomsToDeleteCount = 0
|
||||||
|
|
||||||
init {
|
init {
|
||||||
groupChatAvailable.value = LinphoneUtils.isGroupChatAvailable()
|
groupChatAvailable.value = LinphoneUtils.isGroupChatAvailable()
|
||||||
updateChatRooms()
|
updateChatRooms()
|
||||||
coreContext.core.addListener(listener)
|
coreContext.core.addListener(listener)
|
||||||
coreContext.contactsManager.addListener(contactsListener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
coreContext.contactsManager.removeListener(contactsListener)
|
|
||||||
coreContext.core.removeListener(listener)
|
coreContext.core.removeListener(listener)
|
||||||
|
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
|
@ -139,8 +144,12 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateChatRooms() {
|
fun updateChatRooms() {
|
||||||
val list = arrayListOf<ChatRoom>()
|
chatRooms.value.orEmpty().forEach(ChatRoomData::destroy)
|
||||||
list.addAll(coreContext.core.chatRooms)
|
|
||||||
|
val list = arrayListOf<ChatRoomData>()
|
||||||
|
for (chatRoom in coreContext.core.chatRooms) {
|
||||||
|
list.add(ChatRoomData(chatRoom))
|
||||||
|
}
|
||||||
chatRooms.value = list
|
chatRooms.value = list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,15 +160,16 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderChatRooms() {
|
private fun reorderChatRooms() {
|
||||||
val list = arrayListOf<ChatRoom>()
|
val list = arrayListOf<ChatRoomData>()
|
||||||
list.addAll(chatRooms.value.orEmpty())
|
list.addAll(chatRooms.value.orEmpty())
|
||||||
list.sortByDescending { chatRoom -> chatRoom.lastUpdateTime }
|
list.sortByDescending { data -> data.chatRoom.lastUpdateTime }
|
||||||
chatRooms.value = list
|
chatRooms.value = list
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
||||||
for ((index, cr) in chatRooms.value.orEmpty().withIndex()) {
|
val id = LinphoneUtils.getChatRoomId(chatRoom)
|
||||||
if (LinphoneUtils.areChatRoomsTheSame(cr, chatRoom)) {
|
for ((index, data) in chatRooms.value.orEmpty().withIndex()) {
|
||||||
|
if (id == data.id) {
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,11 +224,6 @@ class LinphoneUtils {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun areChatRoomsTheSame(chatRoom1: ChatRoom, chatRoom2: ChatRoom): Boolean {
|
|
||||||
return chatRoom1.localAddress.weakEqual(chatRoom2.localAddress) &&
|
|
||||||
chatRoom1.peerAddress.weakEqual(chatRoom2.peerAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getChatRoomId(room: ChatRoom): String {
|
fun getChatRoomId(room: ChatRoom): String {
|
||||||
return getChatRoomId(room.localAddress, room.peerAddress)
|
return getChatRoomId(room.localAddress, room.peerAddress)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue