diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt index 4b9d0d791..7d5c496a2 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt @@ -185,7 +185,10 @@ class ConversationFragment : SlidingPaneChildFragment() { private val dataObserver = object : AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (positionStart == adapter.itemCount - itemCount) { + if (positionStart > 0 && positionStart == adapter.itemCount - itemCount) { + Log.i( + "$TAG Item(s) inserted at the end, notify item changed at position [${positionStart - 1}]" + ) adapter.notifyItemChanged(positionStart - 1) // For grouping purposes } @@ -399,11 +402,10 @@ class ConversationFragment : SlidingPaneChildFragment() { } } - viewModel.events.observe(viewLifecycleOwner) { items -> - if (items != adapter.currentList || items.size != adapter.itemCount) { - adapter.submitList(items) - Log.i("$TAG Events (messages) list updated with [${items.size}] items") - } + viewModel.updateEvents.observe(viewLifecycleOwner) { + val items = viewModel.eventsList + adapter.submitList(items) + Log.i("$TAG Events (messages) list updated, contains [${items.size}] items") (view.parent as? ViewGroup)?.doOnPreDraw { sharedViewModel.openSlidingPaneEvent.value = Event(true) @@ -720,9 +722,11 @@ class ConversationFragment : SlidingPaneChildFragment() { @UiThread override fun onScrolledToEnd() { - viewModel.isUserScrollingUp.value = false - Log.i("$TAG Last message is visible, considering conversation as read") - viewModel.markAsRead() + if (viewModel.isUserScrollingUp.value == true) { + viewModel.isUserScrollingUp.value = false + Log.i("$TAG Last message is visible, considering conversation as read") + viewModel.markAsRead() + } } } binding.eventsList.addOnScrollListener(scrollListener) diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt index 6ae5a7406..0c6a74023 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt @@ -59,7 +59,7 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo val avatarModel = MutableLiveData() - val events = MutableLiveData>() + val isEmpty = MutableLiveData() val isMuted = MutableLiveData() @@ -117,7 +117,11 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo MutableLiveData>() } - private var eventsList = arrayListOf() + val updateEvents: MutableLiveData> by lazy { + MutableLiveData>() + } + + var eventsList = arrayListOf() private val chatRoomListener = object : ChatRoomListenerStub() { @WorkerThread @@ -255,7 +259,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo Log.i("$TAG Removing message from conversation events list") list.remove(found) eventsList = list - events.postValue(eventsList) + updateEvents.postValue(Event(true)) + isEmpty.postValue(eventsList.isEmpty()) } else { Log.e("$TAG Failed to find matching message in conversation events list") } @@ -363,7 +368,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo Log.i("$TAG Removing chat message id [${chatMessageModel.id}] from events list") list.remove(found) eventsList = list - events.postValue(eventsList) + updateEvents.postValue(Event(true)) + isEmpty.postValue(eventsList.isEmpty()) } else { Log.e( "$TAG Failed to find chat message id [${chatMessageModel.id}] in events list!" @@ -454,23 +460,20 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo val lastEvent = list.lastOrNull() val newEvent = eventsList.firstOrNull() - if (lastEvent != null && newEvent != null && shouldWeGroupTwoEvents( + if (lastEvent != null && lastEvent.model is MessageModel && newEvent != null && newEvent.model is MessageModel && shouldWeGroupTwoEvents( newEvent.eventLog, lastEvent.eventLog ) ) { - if (lastEvent.model is MessageModel) { - lastEvent.model.groupedWithNextMessage.postValue(true) - } - if (newEvent.model is MessageModel) { - newEvent.model.groupedWithPreviousMessage.postValue(true) - } + lastEvent.model.groupedWithNextMessage.postValue(true) + newEvent.model.groupedWithPreviousMessage.postValue(true) } Log.i("$TAG More data loaded, adding it to conversation events list") list.addAll(eventsList) eventsList = list - events.postValue(eventsList) + updateEvents.postValue(Event(true)) + isEmpty.postValue(eventsList.isEmpty()) } } } @@ -498,7 +501,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo val group = LinphoneUtils.isChatRoomAGroup(chatRoom) isGroup.postValue(group) - val empty = chatRoom.hasCapability(ChatRoom.Capabilities.Conference.toInt()) && chatRoom.participants.isEmpty() + val empty = + chatRoom.hasCapability(ChatRoom.Capabilities.Conference.toInt()) && chatRoom.participants.isEmpty() val readOnly = chatRoom.isReadOnly || empty isReadOnly.postValue(readOnly) if (readOnly) { @@ -556,7 +560,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo val list = getEventsListFromHistory(history, filter) Log.i("$TAG Extracted [${list.size}] events from conversation history in database") eventsList = list - events.postValue(eventsList) + updateEvents.postValue(Event(true)) + isEmpty.postValue(eventsList.isEmpty()) if (filter.isNotEmpty() && eventsList.isEmpty()) { noMatchingResultForFilter.postValue(true) @@ -567,6 +572,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo @WorkerThread private fun addEvents(eventLogs: Array) { + Log.i("$TAG Adding [${eventLogs.size}] events") + // Need to use a new list, otherwise ConversationFragment's dataObserver isn't triggered... val list = arrayListOf() list.addAll(eventsList) val lastEvent = list.lastOrNull() @@ -576,22 +583,20 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo searchFilter.value.orEmpty().trim() ) val newEvent = newList.firstOrNull() - if (lastEvent != null && newEvent != null && shouldWeGroupTwoEvents( + + if (lastEvent != null && lastEvent.model is MessageModel && newEvent != null && newEvent.model is MessageModel && shouldWeGroupTwoEvents( newEvent.eventLog, lastEvent.eventLog ) ) { - if (lastEvent.model is MessageModel) { - lastEvent.model.groupedWithNextMessage.postValue(true) - } - if (newEvent.model is MessageModel) { - newEvent.model.groupedWithPreviousMessage.postValue(true) - } + lastEvent.model.groupedWithNextMessage.postValue(true) + newEvent.model.groupedWithPreviousMessage.postValue(true) } list.addAll(newList) eventsList = list - events.postValue(eventsList) + updateEvents.postValue(Event(true)) + isEmpty.postValue(eventsList.isEmpty()) } @WorkerThread @@ -637,7 +642,10 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo } @WorkerThread - private fun getEventsListFromHistory(history: Array, filter: String = ""): ArrayList { + private fun getEventsListFromHistory( + history: Array, + filter: String = "" + ): ArrayList { val eventsList = arrayListOf() val groupedEventLogs = arrayListOf() @@ -708,7 +716,10 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo } @WorkerThread - private fun updatePreviousAndNextMessages(list: ArrayList, found: EventLogModel) { + private fun updatePreviousAndNextMessages( + list: ArrayList, + found: EventLogModel + ) { val index = list.indexOf(found) if (found.model is MessageModel) { val messageModel = found.model diff --git a/app/src/main/res/layout/chat_conversation_fragment.xml b/app/src/main/res/layout/chat_conversation_fragment.xml index a0dad9ca8..ef3951b9a 100644 --- a/app/src/main/res/layout/chat_conversation_fragment.xml +++ b/app/src/main/res/layout/chat_conversation_fragment.xml @@ -232,7 +232,7 @@ android:onClick="@{endToEndEncryptedEventClickListener}" android:layout_width="0dp" android:layout_height="wrap_content" - android:visibility="@{viewModel.events.size() == 0 && viewModel.isEndToEndEncrypted ? View.VISIBLE : View.GONE}" + android:visibility="@{viewModel.isEmpty && viewModel.isEndToEndEncrypted ? View.VISIBLE : View.GONE}" layout="@layout/chat_conversation_secured_first_event" app:layout_constraintTop_toTopOf="@id/events_list" app:layout_constraintStart_toStartOf="@id/events_list"