From e36e77ef27fe4be0e918fa9ca572a02fbcc9f0e8 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Wed, 20 May 2026 11:53:36 +0200 Subject: [PATCH 1/2] Fixing call started indicator behavior + simplified logic to be depending on chatViewModel Signed-off-by: rapterjet2004 --- .../com/nextcloud/talk/chat/ChatActivity.kt | 24 +++++- .../talk/chat/MessageInputFragment.kt | 77 ++++++++++--------- .../talk/chat/viewmodels/ChatViewModel.kt | 48 ++++++++++++ .../chat/viewmodels/MessageInputViewModel.kt | 8 -- 4 files changed, 107 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 4cf4bf34521..11ab26d52ad 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -49,6 +49,17 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ContextThemeWrapper +import androidx.appcompat.widget.SearchView +import androidx.cardview.widget.CardView +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.scrollBy +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState @@ -67,6 +78,12 @@ import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.unit.dp +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.content.ContextCompat import androidx.compose.ui.platform.LocalDensity import androidx.core.content.FileProvider import androidx.core.content.PermissionChecker @@ -119,6 +136,7 @@ import com.nextcloud.talk.conversationinfo.ConversationInfoActivity import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.dagger.modules.ViewModelFactoryWithParams +import com.nextcloud.talk.data.database.mappers.toDomainModel import com.nextcloud.talk.data.database.model.SendStatus import com.nextcloud.talk.data.network.NetworkMonitor import com.nextcloud.talk.data.user.model.User @@ -1192,8 +1210,8 @@ class ChatActivity : pendingTargetMessageId = extras?.getString(BundleKeys.KEY_MESSAGE_ID)?.toLongOrNull()?.takeIf { it > 0L } ?: extras?.getLong(BundleKeys.KEY_MESSAGE_ID)?.takeIf { it > 0L } - pendingTargetThreadId = extras?.getString(BundleKeys.KEY_THREAD_ID)?.toLongOrNull()?.takeIf { it > 0L } - ?: extras?.getLong(BundleKeys.KEY_THREAD_ID)?.takeIf { it > 0L } + pendingTargetThreadId = extras?.getString(KEY_THREAD_ID)?.toLongOrNull()?.takeIf { it > 0L } + ?: extras?.getLong(KEY_THREAD_ID)?.takeIf { it > 0L } pendingTargetSearchQuery = extras?.getString(BundleKeys.KEY_SEARCH_QUERY) } @@ -1332,7 +1350,6 @@ class ChatActivity : currentConversation = state.conversationModel chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) participantPermissions = ParticipantPermissions(spreedCapabilities, state.conversationModel!!) - supportFragmentManager.commit { setReorderingAllowed(true) // optimizes out redundant replace operations replace(R.id.fragment_container_activity_chat, messageInputFragment) @@ -1440,7 +1457,6 @@ class ChatActivity : when (state) { is ChatViewModel.JoinRoomSuccessState -> { currentConversation = state.conversationModel - sessionIdAfterRoomJoined = currentConversation!!.sessionId ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation!!.sessionId ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = currentConversation!!.token diff --git a/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt b/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt index cc8228efb8c..536011e3c5e 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt @@ -80,10 +80,10 @@ import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.CharPolicy import com.nextcloud.talk.utils.DateUtils +import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.EmojiTextInputEditText import com.nextcloud.talk.utils.ImageEmojiEditText import com.nextcloud.talk.utils.SpreedFeatures @@ -299,47 +299,48 @@ class MessageInputFragment : Fragment() { }.collect() } - messageInputViewModel.callStartedFlow.observe(viewLifecycleOwner) { - val (message, show) = it - if (show) { - binding.fragmentCallStarted.callAuthorChip.text = message.actorDisplayName - binding.fragmentCallStarted.callAuthorChipSecondary.text = message.actorDisplayName - val user = currentUserProvider.currentUser.blockingGet() - val url: String = if (message.actorType == "guests" || message.actorType == "guest") { - ApiUtils.getUrlForGuestAvatar(user!!.baseUrl!!, message.actorDisplayName, true) - } else { - ApiUtils.getUrlForAvatar( - user!!.baseUrl!!, - message.actorId, - false, - darkMode = DisplayUtils.isDarkModeOn(requireContext()) - ) - } + viewLifecycleOwner.lifecycleScope.launch { + chatActivity.chatViewModel.lastCallSystemMessage.collect { + if (it.shouldShow) { + binding.fragmentCallStarted.callAuthorChip.text = it.actorDisplayName + binding.fragmentCallStarted.callAuthorChipSecondary.text = it.actorDisplayName + val user = currentUserProvider.currentUser.blockingGet() + val url: String = if (it.actorType == "guests" || it.actorType == "guest") { + ApiUtils.getUrlForGuestAvatar(user!!.baseUrl!!, it.actorDisplayName, true) + } else { + ApiUtils.getUrlForAvatar( + user!!.baseUrl!!, + it.actorId, + false, + darkMode = DisplayUtils.isDarkModeOn(requireContext()) + ) + } - val imageRequest: ImageRequest = ImageRequest.Builder(requireContext()) - .data(url) - .crossfade(true) - .transformations(CircleCropTransformation()) - .target(object : Target { - override fun onStart(placeholder: Drawable?) { - // unused atm - } + val imageRequest: ImageRequest = ImageRequest.Builder(requireContext()) + .data(url) + .crossfade(true) + .transformations(CircleCropTransformation()) + .target(object : Target { + override fun onStart(placeholder: Drawable?) { + // unused atm + } - override fun onError(error: Drawable?) { - // unused atm - } + override fun onError(error: Drawable?) { + // unused atm + } - override fun onSuccess(result: Drawable) { - binding.fragmentCallStarted.callAuthorChip.chipIcon = result - binding.fragmentCallStarted.callAuthorChipSecondary.chipIcon = result - } - }) - .build() + override fun onSuccess(result: Drawable) { + binding.fragmentCallStarted.callAuthorChip.chipIcon = result + binding.fragmentCallStarted.callAuthorChipSecondary.chipIcon = result + } + }) + .build() - imageLoader(requireContext()).enqueue(imageRequest) - binding.fragmentCallStarted.root.visibility = View.VISIBLE - } else { - binding.fragmentCallStarted.root.visibility = View.GONE + imageLoader(requireContext()).enqueue(imageRequest) + binding.fragmentCallStarted.root.visibility = View.VISIBLE + } else { + binding.fragmentCallStarted.root.visibility = View.GONE + } } } } diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index 0827e467e0a..1b256e8110b 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -1011,6 +1011,24 @@ class ChatViewModel @AssistedInject constructor( val expandedParents: Set = emptySet() ) + data class CallStartedIndicatorData( + val actorDisplayName: String, + val actorType: String, + val actorId: String, + val shouldShow: Boolean + ) + + private val _lastCallSystemMessage = MutableStateFlow(null) + + val lastCallSystemMessage = _lastCallSystemMessage.map { msg -> + CallStartedIndicatorData( + msg?.actorDisplayName ?: "", + msg?.actorType ?: "", + msg?.actorId ?: "", + msg != null + ) + } + private data class ProcessedMessages(val items: List, val missingParentIds: List) private fun observeMessages() { @@ -1328,6 +1346,21 @@ class ChatViewModel @AssistedInject constructor( val chatMessageMap = chatMessageList.associateBy { it.jsonMessageId }.toMutableMap() val chatMessageIterator = chatMessageMap.iterator() + chatMessageList.lastOrNull { + it.systemMessageType in + listOf( + ChatMessage.SystemMessageType.CALL_STARTED, + ChatMessage.SystemMessageType.CALL_JOINED, + ChatMessage.SystemMessageType.CALL_LEFT, + ChatMessage.SystemMessageType.CALL_ENDED, + ChatMessage.SystemMessageType.CALL_TRIED, + ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE, + ChatMessage.SystemMessageType.CALL_MISSED + ) + }?.let { callMessage -> + processCallSystemMessage(callMessage) + } + while (chatMessageIterator.hasNext()) { val currentMessage = chatMessageIterator.next() @@ -1338,6 +1371,21 @@ class ChatViewModel @AssistedInject constructor( return chatMessageMap.values.toList() } + private fun processCallSystemMessage(recent: ChatMessage) { + when (recent.systemMessageType) { + ChatMessage.SystemMessageType.CALL_STARTED -> { + _lastCallSystemMessage.tryEmit(recent) + } + ChatMessage.SystemMessageType.CALL_ENDED, + ChatMessage.SystemMessageType.CALL_MISSED, + ChatMessage.SystemMessageType.CALL_TRIED, + ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE -> { + _lastCallSystemMessage.tryEmit(null) + } + else -> {} + } + } + private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry): Boolean = currentMessage.value.parentMessageId != null && currentMessage.value.systemMessageType == ChatMessage diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt index dfd86f7e699..e6128411dea 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt @@ -138,10 +138,6 @@ class MessageInputViewModel : val isVoicePreviewPlaying: LiveData get() = _isVoicePreviewPlaying - private val _callStartedFlow: MutableLiveData> = MutableLiveData() - val callStartedFlow: LiveData> - get() = _callStartedFlow - object ScheduleChatMessageStartState : ViewState class ScheduleChatMessageSuccessState(val scheduledAt: Long) : ViewState object ScheduleChatMessageErrorState : ViewState @@ -287,10 +283,6 @@ class MessageInputViewModel : _getRecordingTime.postValue(time) } - fun showCallStartedIndicator(recent: ChatMessage, show: Boolean) { - _callStartedFlow.postValue(Pair(recent, show)) - } - fun startThreadCreation() { _createThreadViewState.postValue(CreateThreadEditState()) } From b84c53430e1135d6659bd46b860013791ef91365 Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Fri, 5 Jun 2026 11:56:34 -0500 Subject: [PATCH 2/2] linter Signed-off-by: rapterjet2004 --- .../com/nextcloud/talk/chat/ChatActivity.kt | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 11ab26d52ad..4b5140eeae3 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -49,17 +49,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog -import androidx.appcompat.view.ContextThemeWrapper -import androidx.appcompat.widget.SearchView -import androidx.cardview.widget.CardView -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.scrollBy -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState @@ -78,12 +67,6 @@ import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.unit.dp -import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.content.ContextCompat import androidx.compose.ui.platform.LocalDensity import androidx.core.content.FileProvider import androidx.core.content.PermissionChecker @@ -136,7 +119,6 @@ import com.nextcloud.talk.conversationinfo.ConversationInfoActivity import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.dagger.modules.ViewModelFactoryWithParams -import com.nextcloud.talk.data.database.mappers.toDomainModel import com.nextcloud.talk.data.database.model.SendStatus import com.nextcloud.talk.data.network.NetworkMonitor import com.nextcloud.talk.data.user.model.User