Should improve scrolling performances in conversation

This commit is contained in:
Sylvain Berfini 2021-06-10 17:20:19 +02:00
parent e2a04f2e95
commit 5cf34edc07
7 changed files with 107 additions and 74 deletions

View file

@ -36,6 +36,7 @@ import org.linphone.R
import org.linphone.activities.main.adapters.SelectionListAdapter
import org.linphone.activities.main.chat.data.ChatMessageData
import org.linphone.activities.main.chat.data.EventData
import org.linphone.activities.main.chat.data.EventLogData
import org.linphone.activities.main.chat.data.OnContentClickedListener
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
import org.linphone.core.ChatMessage
@ -51,7 +52,7 @@ import org.linphone.utils.Event
class ChatMessagesListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<EventLog, RecyclerView.ViewHolder>(selectionVM, ChatMessageDiffCallback()) {
) : SelectionListAdapter<EventLogData, RecyclerView.ViewHolder>(selectionVM, ChatMessageDiffCallback()) {
companion object {
const val MAX_TIME_TO_GROUP_MESSAGES = 60 // 1 minute
}
@ -127,7 +128,7 @@ class ChatMessagesListAdapter(
override fun getItemViewType(position: Int): Int {
val eventLog = getItem(position)
return eventLog.type.toInt()
return eventLog.eventLog.type.toInt()
}
fun disableContextMenu() {
@ -137,12 +138,13 @@ class ChatMessagesListAdapter(
inner class ChatMessageViewHolder(
val binding: ChatMessageListCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(eventLog: EventLog) {
fun bind(eventLog: EventLogData) {
with(binding) {
if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
val chatMessage = eventLog.chatMessage
chatMessage ?: return
val chatMessageViewModel = ChatMessageData(chatMessage, contentClickedListener)
if (eventLog.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val chatMessageViewModel = eventLog.data as ChatMessageData
chatMessageViewModel.setContentClickListener(contentClickedListener)
val chatMessage = chatMessageViewModel.chatMessage
data = chatMessageViewModel
lifecycleOwner = viewLifecycleOwner
@ -165,8 +167,8 @@ class ChatMessagesListAdapter(
if (adapterPosition > 0) {
val previousItem = getItem(adapterPosition - 1)
if (previousItem.type == EventLog.Type.ConferenceChatMessage) {
val previousMessage = previousItem.chatMessage
if (previousItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val previousMessage = previousItem.eventLog.chatMessage
if (previousMessage != null && previousMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (chatMessage.time - previousMessage.time < MAX_TIME_TO_GROUP_MESSAGES) {
hasPrevious = true
@ -177,8 +179,8 @@ class ChatMessagesListAdapter(
if (adapterPosition >= 0 && adapterPosition < itemCount - 1) {
val nextItem = getItem(adapterPosition + 1)
if (nextItem.type == EventLog.Type.ConferenceChatMessage) {
val nextMessage = nextItem.chatMessage
if (nextItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val nextMessage = nextItem.eventLog.chatMessage
if (nextMessage != null && nextMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (nextMessage.time - chatMessage.time < MAX_TIME_TO_GROUP_MESSAGES) {
hasNext = true
@ -319,9 +321,9 @@ class ChatMessagesListAdapter(
inner class EventViewHolder(
private val binding: ChatEventListCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(eventLog: EventLog) {
fun bind(eventLog: EventLogData) {
with(binding) {
val eventViewModel = EventData(eventLog)
val eventViewModel = eventLog.data as EventData
data = eventViewModel
binding.lifecycleOwner = viewLifecycleOwner
@ -344,24 +346,24 @@ class ChatMessagesListAdapter(
}
}
private class ChatMessageDiffCallback : DiffUtil.ItemCallback<EventLog>() {
private class ChatMessageDiffCallback : DiffUtil.ItemCallback<EventLogData>() {
override fun areItemsTheSame(
oldItem: EventLog,
newItem: EventLog
oldItem: EventLogData,
newItem: EventLogData
): Boolean {
return if (oldItem.type == EventLog.Type.ConferenceChatMessage &&
newItem.type == EventLog.Type.ConferenceChatMessage) {
oldItem.chatMessage?.time == newItem.chatMessage?.time &&
oldItem.chatMessage?.isOutgoing == newItem.chatMessage?.isOutgoing
} else oldItem.notifyId == newItem.notifyId
return if (oldItem.eventLog.type == EventLog.Type.ConferenceChatMessage &&
newItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
oldItem.eventLog.chatMessage?.time == newItem.eventLog.chatMessage?.time &&
oldItem.eventLog.chatMessage?.isOutgoing == newItem.eventLog.chatMessage?.isOutgoing
} else oldItem.eventLog.notifyId == newItem.eventLog.notifyId
}
override fun areContentsTheSame(
oldItem: EventLog,
newItem: EventLog
oldItem: EventLogData,
newItem: EventLogData
): Boolean {
return if (newItem.type == EventLog.Type.ConferenceChatMessage) {
newItem.chatMessage?.state == ChatMessage.State.Displayed
} else false
return if (newItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
newItem.eventLog.chatMessage?.state == ChatMessage.State.Displayed
} else true
}
}

View file

@ -39,8 +39,10 @@ import org.linphone.utils.ImageUtils
class ChatMessageContentData(
private val chatMessage: ChatMessage,
private val contentIndex: Int,
private val listener: OnContentClickedListener?
) {
var listener: OnContentClickedListener? = null
val isImage = MutableLiveData<Boolean>()
val isVideo = MutableLiveData<Boolean>()
val isAudio = MutableLiveData<Boolean>()

View file

@ -31,10 +31,9 @@ import org.linphone.core.ChatMessageListenerStub
import org.linphone.utils.AppUtils
import org.linphone.utils.TimestampUtils
class ChatMessageData(
val chatMessage: ChatMessage,
class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMessage.fromAddress) {
private var contentListener: OnContentClickedListener? = null
) : GenericContactData(chatMessage.fromAddress) {
val sendInProgress = MutableLiveData<Boolean>()
val transferInProgress = MutableLiveData<Boolean>()
@ -120,6 +119,14 @@ class ChatMessageData(
}
}
fun setContentClickListener(listener: OnContentClickedListener) {
contentListener = listener
for (data in contents.value.orEmpty()) {
data.listener = listener
}
}
private fun updateChatMessageState(state: ChatMessage.State) {
transferInProgress.value = state == ChatMessage.State.FileTransferInProgress
@ -145,7 +152,9 @@ class ChatMessageData(
for (index in 0 until contentsList.size) {
val content = contentsList[index]
if (content.isFileTransfer || content.isFile) {
list.add(ChatMessageContentData(chatMessage, index, contentListener))
val data = ChatMessageContentData(chatMessage, index)
data.listener = contentListener
list.add(data)
} else if (content.isText) {
val spannable = Spannable.Factory.getInstance().newSpannable(content.utf8Text)
LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS)

View file

@ -23,12 +23,19 @@ import android.content.Context
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contact.Contact
import org.linphone.contact.GenericContactData
import org.linphone.core.EventLog
import org.linphone.core.tools.Log
import org.linphone.utils.LinphoneUtils
class EventData(private val eventLog: EventLog) {
class EventData(private val eventLog: EventLog) : GenericContactData(
if (eventLog.type == EventLog.Type.ConferenceSecurityEvent) {
eventLog.securityEventFaultyDeviceAddress!!
} else {
if (eventLog.participantAddress == null) {
eventLog.peerAddress!!
} else {
eventLog.participantAddress!!
}
}) {
val text = MutableLiveData<String>()
val isSecurity: Boolean by lazy {
@ -38,32 +45,12 @@ class EventData(private val eventLog: EventLog) {
}
}
private val contact: Contact? by lazy {
val address = eventLog.participantAddress ?: eventLog.securityEventFaultyDeviceAddress
if (address != null) {
coreContext.contactsManager.findContactByAddress(address)
} else {
Log.e("[Event ViewModel] Unexpected null address for event $eventLog")
null
}
}
private val displayName: String by lazy {
val address = eventLog.participantAddress ?: eventLog.securityEventFaultyDeviceAddress
if (address != null) {
LinphoneUtils.getDisplayName(address)
} else {
Log.e("[Event ViewModel] Unexpected null address for event $eventLog")
""
}
}
init {
updateEventText()
}
private fun getName(): String {
return contact?.fullName ?: displayName
return contact.value?.fullName ?: displayName
}
private fun updateEventText() {

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2010-2021 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 org.linphone.contact.GenericContactData
import org.linphone.core.EventLog
class EventLogData(val eventLog: EventLog) {
val data: GenericContactData = if (eventLog.type == EventLog.Type.ConferenceChatMessage) {
ChatMessageData(eventLog.chatMessage!!)
} else {
EventData(eventLog)
}
}

View file

@ -47,6 +47,7 @@ import org.linphone.activities.*
import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.chat.ChatScrollListener
import org.linphone.activities.main.chat.adapters.ChatMessagesListAdapter
import org.linphone.activities.main.chat.data.EventLogData
import org.linphone.activities.main.chat.viewmodels.*
import org.linphone.activities.main.fragments.MasterFragment
import org.linphone.activities.main.viewmodels.DialogViewModel
@ -345,7 +346,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
override fun deleteItems(indexesOfItemToDelete: ArrayList<Int>) {
val list = ArrayList<EventLog>()
val list = ArrayList<EventLogData>()
for (index in indexesOfItemToDelete) {
val eventLog = adapter.currentList[index]
list.add(eventLog)

View file

@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import java.util.*
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.activities.main.chat.data.EventLogData
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.mediastream.Version
@ -45,7 +46,7 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
private const val MESSAGES_PER_PAGE = 20
}
val events = MutableLiveData<ArrayList<EventLog>>()
val events = MutableLiveData<ArrayList<EventLogData>>()
val messageUpdatedEvent: MutableLiveData<Event<Int>> by lazy {
MutableLiveData<Event<Int>>()
@ -67,8 +68,8 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
chatMessage ?: return
chatMessage.userData = events.value.orEmpty().size
val existingEvent = events.value.orEmpty().find {
it.type == EventLog.Type.ConferenceChatMessage && it.chatMessage == chatMessage
val existingEvent = events.value.orEmpty().find { data ->
data.eventLog == eventLog
}
if (existingEvent != null) {
Log.w("[Chat Messages] Found already present chat message, don't add it it's probably the result of an auto download")
@ -165,19 +166,19 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
LinphoneUtils.deleteFilesAttachedToChatMessage(chatMessage)
chatRoom.deleteMessage(chatMessage)
val list = arrayListOf<EventLog>()
val list = arrayListOf<EventLogData>()
list.addAll(events.value.orEmpty())
list.removeAt(position)
events.value = list
}
fun deleteEventLogs(listToDelete: ArrayList<EventLog>) {
val list = arrayListOf<EventLog>()
fun deleteEventLogs(listToDelete: ArrayList<EventLogData>) {
val list = arrayListOf<EventLogData>()
list.addAll(events.value.orEmpty())
for (eventLog in listToDelete) {
LinphoneUtils.deleteFilesAttachedToEventLog(eventLog)
eventLog.deleteFromDatabase()
LinphoneUtils.deleteFilesAttachedToEventLog(eventLog.eventLog)
eventLog.eventLog.deleteFromDatabase()
list.remove(eventLog)
}
@ -195,9 +196,9 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
}
val history: Array<EventLog> = chatRoom.getHistoryRangeEvents(totalItemsCount, upperBound)
val list = arrayListOf<EventLog>()
for (message in history) {
list.add(message)
val list = arrayListOf<EventLogData>()
for (eventLog in history) {
list.add(EventLogData(eventLog))
}
list.addAll(events.value.orEmpty())
events.value = list
@ -205,19 +206,19 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
}
private fun addEvent(eventLog: EventLog) {
val list = arrayListOf<EventLog>()
val list = arrayListOf<EventLogData>()
list.addAll(events.value.orEmpty())
if (!list.contains(eventLog)) {
list.add(eventLog)
list.add(EventLogData(eventLog))
}
events.value = list
}
private fun getEvents(): ArrayList<EventLog> {
val list = arrayListOf<EventLog>()
private fun getEvents(): ArrayList<EventLogData> {
val list = arrayListOf<EventLogData>()
val history = chatRoom.getHistoryEvents(MESSAGES_PER_PAGE)
for (message in history) {
list.add(message)
for (eventLog in history) {
list.add(EventLogData(eventLog))
}
return list
}