From 6583442a4b0956fb1643b74e6bb27f8f04854b6c Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Thu, 16 Apr 2026 10:42:01 -0500 Subject: [PATCH 1/4] Reimplementing download indicator Reimplementing openWhenDownloaded functionality Signed-off-by: rapterjet2004 --- .../com/nextcloud/talk/chat/ChatActivity.kt | 71 +++++++++++++++++-- .../adapters/SharedItemsViewHolder.kt | 6 +- .../nextcloud/talk/ui/chat/ChatMessageView.kt | 6 +- .../com/nextcloud/talk/ui/chat/ChatView.kt | 14 ++-- .../nextcloud/talk/ui/chat/MediaMessage.kt | 17 ++++- .../nextcloud/talk/utils/FileViewerUtils.kt | 70 ++++++++++++------ 6 files changed, 148 insertions(+), 36 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 8a52e22bd0c..204b72aebc3 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -67,11 +67,16 @@ import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView @@ -121,6 +126,8 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApiCoroutines import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.chat.data.model.ChatMessage +import com.nextcloud.talk.chat.data.model.FileParameters +import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet import com.nextcloud.talk.chat.ui.model.MessageTypeContent import com.nextcloud.talk.chat.viewmodels.ChatViewModel @@ -725,6 +732,8 @@ class ChatActivity : chatListComposeScope = composeScope } + SideEffect { chatListState = listState } + CompositionLocalProvider( LocalViewThemeUtils provides viewThemeUtils, LocalMessageUtils provides messageUtils, @@ -733,6 +742,18 @@ class ChatActivity : val isOneToOneConversation = uiState.isOneToOneConversation Log.d(TAG, "isOneToOneConversation=" + isOneToOneConversation) + // list of the file ids of messages being downloaded + val downloadingFileState = remember { mutableStateOf(listOf()) } + + // openWhenDownloaded is a derived boolean state of the visible chat message list on the condition + // that if any of the messages that are present contain a fileId that is within downloadingFileState + val openWhenDownloadState = remember { mutableStateOf(false) } + + val visibleIds = listState.visibleItemsWithThreshold() + LaunchedEffect(visibleIds, downloadingFileState.value) { + openWhenDownloadState.value = (downloadingFileState.value.intersect(visibleIds).isNotEmpty()) + } + ChatView( state = ChatViewState( chatItems = uiState.items, @@ -742,7 +763,8 @@ class ChatActivity : highlightedMessageId = uiState.highlightedMessageId, highlightedSearchTerm = uiState.highlightedSearchTerm, hasChatPermission = this::participantPermissions.isInitialized && - participantPermissions.hasChatPermission() + participantPermissions.hasChatPermission(), + downloadingFileState = downloadingFileState.value ), callbacks = ChatViewCallbacks( onLoadMore = { messageId, direction -> loadMoreMessages(messageId, direction) }, @@ -753,7 +775,7 @@ class ChatActivity : messageCallbacks = ChatMessageCallbacks( onLongClick = { openMessageActionsDialog(it) }, onSwipeReply = { handleSwipeToReply(it) }, - onFileClick = { downloadAndOpenFile(it) }, + onFileClick = { downloadAndOpenFile(it, openWhenDownloadState, downloadingFileState) }, onPollClick = { pollId, pollName -> openPollDialog(pollId, pollName) }, onVoicePlayPauseClick = { onVoicePlayPauseClickCompose(it) }, onVoiceSeek = { _, progress -> chatViewModel.seekToMediaPlayer(progress) }, @@ -791,6 +813,39 @@ class ChatActivity : } } + @Composable + private fun LazyListState.visibleItemsWithThreshold(): List = + remember(this) { + derivedStateOf { + val visibleItemsInfo = layoutInfo.visibleItemsInfo + if (layoutInfo.totalItemsCount == 0) { + emptyList() + } else { + visibleItemsInfo.toMutableList().map { it.key as String } + } + } + }.value.mapNotNull { key -> + val messageItem = chatViewModel.uiState.value.items.firstOrNull { it.stableKey() == key } + val message = messageItem?.messageOrNull() + var result: String? = null + message?.let { + if (message.messageParameters.isNotEmpty()) { + runCatching { + message.messageParameters as HashMap>? + val fileParameters = FileParameters(message.messageParameters) + result = fileParameters.id + }.onFailure { e -> + when (e) { + is ClassCastException -> {} // weird + else -> Log.e(TAG, "Error in LazyListState.visibleItemsWithThreshold $e") + } + } + } + } + + result + } + private fun onLoadQuotedMessage(messageId: Int) { chatViewModel.jumpToQuotedMessage(messageId.toLong()) } @@ -856,10 +911,18 @@ class ChatActivity : chatViewModel.setVoiceMessageSpeed(messageId, nextSpeed) } - fun downloadAndOpenFile(messageId: Int) { + fun downloadAndOpenFile( + messageId: Int, + openWhenDownloadState: MutableState, + downloadState: MutableState> + ) { lifecycleScope.launch { val chatMessage = chatViewModel.getMessageById(messageId.toLong()).first() - FileViewerUtils(this@ChatActivity, conversationUser).openFile(chatMessage) + FileViewerUtils(this@ChatActivity, conversationUser).openFile( + chatMessage, + openWhenDownloadState, + downloadState + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt index 266c44c8e81..8113ee3d840 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt @@ -11,6 +11,7 @@ import android.content.Context import android.view.View import android.widget.ImageView import android.widget.ProgressBar +import androidx.compose.runtime.mutableStateOf import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding import com.nextcloud.talk.data.user.model.User @@ -58,6 +59,7 @@ abstract class SharedItemsViewHolder( This should be done after a refactoring of FileViewerUtils. */ val fileViewerUtils = FileViewerUtils(image.context, user) + val trueState = mutableStateOf(true) clickTarget.setOnClickListener { fileViewerUtils.openFile( @@ -74,7 +76,7 @@ abstract class SharedItemsViewHolder( // null, // image // ), - true + trueState ) } @@ -82,7 +84,7 @@ abstract class SharedItemsViewHolder( item.name, item.id, item.mimeType, - true, + trueState, FileViewerUtils.ProgressUi(progressBar, null, image) ) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageView.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageView.kt index b79e0271d82..9f9b188a9fa 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageView.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageView.kt @@ -54,11 +54,12 @@ private const val QUOTE_HIGHLIGHT_FADE_OUT_MILLIS = 1500 data class ChatMessageContext( val isOneToOneConversation: Boolean = false, val conversationThreadId: Long? = null, - val hasChatPermission: Boolean = true + val hasChatPermission: Boolean = true, + val downloadingFileState: List = listOf() ) @Suppress("Detekt.LongParameterList") -class ChatMessageCallbacks( +data class ChatMessageCallbacks( val onLongClick: ((Int) -> Unit?)? = null, val onSwipeReply: ((Int) -> Unit)? = null, val onFileClick: (Int) -> Unit = {}, @@ -149,6 +150,7 @@ fun ChatMessageView( message = message, isOneToOneConversation = context.isOneToOneConversation, conversationThreadId = context.conversationThreadId, + chatViewDownloadingFileState = context.downloadingFileState, onImageClick = callbacks.onFileClick ) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatView.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatView.kt index 2adad112c87..6aedebbdcd1 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/ChatView.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/ChatView.kt @@ -15,8 +15,8 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleOut import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues @@ -25,8 +25,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape @@ -59,6 +59,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.nextcloud.talk.R import com.nextcloud.talk.chat.ui.model.ChatMessageUi import com.nextcloud.talk.chat.ui.model.MessageStatusIcon import com.nextcloud.talk.chat.ui.model.MessageTypeContent @@ -74,7 +75,6 @@ import java.time.Instant import java.time.LocalDate import java.time.ZoneId import java.time.format.DateTimeFormatter -import com.nextcloud.talk.R private const val LONG_1000 = 1000L private const val LOAD_MORE_BUFFER_ITEMS = 5 @@ -93,10 +93,11 @@ data class ChatViewState( val initialShowUnreadPopup: Boolean = false, val chatMode: ChatViewModel.ChatMode = ChatViewModel.ChatMode.DEFAULT_MODE, val highlightedMessageId: Int? = null, - val highlightedSearchTerm: String? = null + val highlightedSearchTerm: String? = null, + val downloadingFileState: List = listOf() ) -class ChatViewCallbacks( +data class ChatViewCallbacks( val onLoadMore: ((Int, ChatViewModel.LoadMoreDirection) -> Unit?)? = null, val advanceLocalLastReadMessageIfNeeded: ((Int) -> Unit?)? = null, val updateRemoteLastReadMessageIfNeeded: (() -> Unit?)? = null, @@ -349,7 +350,8 @@ fun ChatView( context = ChatMessageContext( isOneToOneConversation = state.isOneToOneConversation, conversationThreadId = state.conversationThreadId, - hasChatPermission = state.hasChatPermission + hasChatPermission = state.hasChatPermission, + downloadingFileState = state.downloadingFileState ), callbacks = ChatMessageCallbacks( onLongClick = callbacks.messageCallbacks.onLongClick, diff --git a/app/src/main/java/com/nextcloud/talk/ui/chat/MediaMessage.kt b/app/src/main/java/com/nextcloud/talk/ui/chat/MediaMessage.kt index 98ecda450e2..b2cbb1f81bd 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/chat/MediaMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/chat/MediaMessage.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -28,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.nextcloud.talk.R +import com.nextcloud.talk.chat.data.model.FileParameters import com.nextcloud.talk.chat.ui.model.ChatMessageUi import com.nextcloud.talk.chat.ui.model.MessageTypeContent import com.nextcloud.talk.contacts.load @@ -38,15 +40,19 @@ private const val FILE_PLACEHOLDER_MESSAGE = "{file}" private val mediaRadiusBig = 8.dp private val mediaRadiusSmall = 2.dp -@Suppress("Detekt.LongMethod") +@Suppress("Detekt.LongMethod", "LongParameterList") @Composable fun MediaMessage( typeContent: MessageTypeContent.Media, message: ChatMessageUi, isOneToOneConversation: Boolean = false, conversationThreadId: Long? = null, + chatViewDownloadingFileState: List, onImageClick: (Int) -> Unit ) { + val fileParameters = + remember { FileParameters(message.messageParameters as HashMap>?) } + val captionText = message.message.takeUnless { it == FILE_PLACEHOLDER_MESSAGE } val hasCaption = captionText != null val mediaInset = 4.dp @@ -118,6 +124,15 @@ fun MediaMessage( tint = Color.White ) } + + if (chatViewDownloadingFileState.contains(fileParameters.id)) { + CircularProgressIndicator( + modifier = Modifier + .size(48.dp) + .align(Alignment.Center), + strokeWidth = 2.dp + ) + } } } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 45602091ba7..7ab6ac240f5 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -15,10 +15,11 @@ import android.util.Log import android.view.View import android.widget.ImageView import android.widget.ProgressBar -import androidx.lifecycle.Observer +import androidx.compose.runtime.MutableState import androidx.core.content.FileProvider import androidx.core.net.toUri import androidx.emoji2.widget.EmojiTextView +import androidx.lifecycle.Observer import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkInfo @@ -61,7 +62,11 @@ import java.util.concurrent.ExecutionException */ class FileViewerUtils(private val context: Context, private val user: User) { - fun openFile(message: ChatMessage) { + fun openFile( + message: ChatMessage, + openWhenDownloadState: MutableState, + downloadState: MutableState> + ) { val fileName = message.fileParameters.name val mimetype = message.fileParameters.mimetype val link = message.fileParameters.link @@ -71,19 +76,26 @@ class FileViewerUtils(private val context: Context, private val user: User) { val fileSize = message.fileParameters.size + Log.d("Julius", "OpenWhenDownloaded: ${openWhenDownloadState.value}") openFile( FileInfo(fileId, fileName, fileSize, path, link, mimetype), - message.openWhenDownloaded + openWhenDownloadState, + downloadState ) } - fun openFile(fileInfo: FileInfo, openWhenDownloaded: Boolean) { + fun openFile( + fileInfo: FileInfo, + openWhenDownloadState: MutableState, + downloadState: MutableState>? = null + ) { if (isSupportedForInternalViewer(fileInfo.mimetype) || canBeHandledByExternalApp(fileInfo.mimetype, fileInfo.fileName) ) { openOrDownloadFile( fileInfo, - openWhenDownloaded + openWhenDownloadState, + downloadState ) } else if (!fileInfo.link.isNullOrEmpty()) { openFileInFilesApp(fileInfo.link, fileInfo.fileId) @@ -106,14 +118,19 @@ class FileViewerUtils(private val context: Context, private val user: User) { return intent.resolveActivity(context.packageManager) != null } - private fun openOrDownloadFile(fileInfo: FileInfo, openWhenDownloaded: Boolean) { + private fun openOrDownloadFile( + fileInfo: FileInfo, + openWhenDownloadState: MutableState, + downloadState: MutableState>? + ) { val file = File(context.cacheDir, fileInfo.fileName) if (file.exists()) { openFileByMimetype(fileInfo.fileName, fileInfo.mimetype, fileInfo.link, fileInfo.fileId) } else { downloadFileToCache( fileInfo, - openWhenDownloaded + openWhenDownloadState, + downloadState ) } } @@ -127,18 +144,17 @@ class FileViewerUtils(private val context: Context, private val user: User) { VIDEO_MP4, VIDEO_QUICKTIME, VIDEO_OGG, - VIDEO_WEBM - -> openMediaView(filename, mimetype) + VIDEO_WEBM -> openMediaView(filename, mimetype) + IMAGE_PNG, IMAGE_JPEG, IMAGE_GIF, - IMAGE_HEIC - -> openImageView(filename, mimetype) + IMAGE_HEIC -> openImageView(filename, mimetype) + TEXT_MARKDOWN, - TEXT_PLAIN - -> openTextView(filename, mimetype, link, fileId) - else - -> openFileByExternalApp(filename, mimetype) + TEXT_PLAIN -> openTextView(filename, mimetype, link, fileId) + + else -> openFileByExternalApp(filename, mimetype) } } else { Log.e(TAG, "can't open file with unknown mimetype") @@ -237,11 +253,18 @@ class FileViewerUtils(private val context: Context, private val user: User) { VIDEO_WEBM, TEXT_MARKDOWN, TEXT_PLAIN -> true + else -> false } @SuppressLint("LongLogTag") - private fun downloadFileToCache(fileInfo: FileInfo, openWhenDownloaded: Boolean) { + private fun downloadFileToCache( + fileInfo: FileInfo, + openWhenDownloadState: MutableState, + downloadState: MutableState>? + ) { + downloadState?.value = downloadState.value + fileInfo.fileId + // check if download worker is already running val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileInfo.fileId) try { @@ -289,24 +312,26 @@ class FileViewerUtils(private val context: Context, private val user: User) { fileInfo.fileName, fileInfo.mimetype, workInfo, - openWhenDownloaded, + openWhenDownloadState, fileInfo.link, fileInfo.fileId ) if (workInfo.state.isFinished) { liveData.removeObserver(this) + downloadState?.value = downloadState.value - fileInfo.fileId } } } liveData.observeForever(observer) } + @Suppress("LongParameterList") private fun updateViewsByProgress( fileName: String, mimetype: String?, workInfo: WorkInfo, // progressUi: ProgressUi, - openWhenDownloaded: Boolean, + openWhenDownloadState: MutableState, link: String? = null, fileId: String = "" ) { @@ -321,8 +346,9 @@ class FileViewerUtils(private val context: Context, private val user: User) { // ) } } + WorkInfo.State.SUCCEEDED -> { - if (openWhenDownloaded) { + if (openWhenDownloadState.value) { openFileByMimetype(fileName, mimetype, link, fileId) } else { Log.d( @@ -335,10 +361,12 @@ class FileViewerUtils(private val context: Context, private val user: User) { // progressUi.messageText?.text = fileName // progressUi.progressBar?.visibility = View.GONE } + WorkInfo.State.FAILED -> { // progressUi.messageText?.text = fileName // progressUi.progressBar?.visibility = View.GONE } + else -> { } } @@ -348,7 +376,7 @@ class FileViewerUtils(private val context: Context, private val user: User) { fileName: String, fileId: String, mimeType: String?, - openWhenDownloaded: Boolean, + openWhenDownloadState: MutableState, progressUi: ProgressUi ) { val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId) @@ -367,7 +395,7 @@ class FileViewerUtils(private val context: Context, private val user: User) { fileName, mimeType, info!!, - openWhenDownloaded + openWhenDownloadState ) } } From 0bc3c76143637149fdcd6b9902dea31da016453b Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Fri, 24 Apr 2026 09:54:13 -0500 Subject: [PATCH 2/4] linter Signed-off-by: rapterjet2004 --- app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 204b72aebc3..7ef83f639d4 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -72,6 +72,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -825,7 +826,7 @@ class ChatActivity : } } }.value.mapNotNull { key -> - val messageItem = chatViewModel.uiState.value.items.firstOrNull { it.stableKey() == key } + val messageItem = chatViewModel.uiState.collectAsState().value.items.firstOrNull { it.stableKey() == key } val message = messageItem?.messageOrNull() var result: String? = null message?.let { From 96f53c2714ea787e4430d0df036fa101099beaa3 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 13 May 2026 13:02:11 +0200 Subject: [PATCH 3/4] cleanup Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt | 3 +-- app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt | 1 - 2 files changed, 1 insertion(+), 3 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 7ef83f639d4..74d861de829 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -129,7 +129,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.chat.data.model.ChatMessage import com.nextcloud.talk.chat.data.model.FileParameters import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet -import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet import com.nextcloud.talk.chat.ui.model.MessageTypeContent import com.nextcloud.talk.chat.viewmodels.ChatViewModel import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel @@ -228,9 +227,9 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 7ab6ac240f5..f35ea277d6f 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -76,7 +76,6 @@ class FileViewerUtils(private val context: Context, private val user: User) { val fileSize = message.fileParameters.size - Log.d("Julius", "OpenWhenDownloaded: ${openWhenDownloadState.value}") openFile( FileInfo(fileId, fileName, fileSize, path, link, mimetype), openWhenDownloadState, From 4e58d070fcf6a2435460d47aa4a1cd0b4a5acb9a Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 13 May 2026 13:19:52 +0200 Subject: [PATCH 4/4] add Safe cast with as? + mapNotNull replaces the unsafe as String cast the unnecessary toMutableList() is dropped Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 74d861de829..0b32dc84c69 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -821,7 +821,7 @@ class ChatActivity : if (layoutInfo.totalItemsCount == 0) { emptyList() } else { - visibleItemsInfo.toMutableList().map { it.key as String } + visibleItemsInfo.mapNotNull { it.key as? String } } } }.value.mapNotNull { key ->