From 0217ab5b6db553219c8930d8681a8ba680ce641a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 13:32:13 +0100 Subject: [PATCH 1/6] fix(file-list): sync icon appearance Signed-off-by: alperozturk96 --- .../nextcloud/client/database/dao/FileDao.kt | 2 +- .../jobs/download/FileDownloadHelper.kt | 6 +- .../folderDownload/FolderDownloadWorker.kt | 12 ++-- .../FileDataStorageManagerExtensions.kt | 2 +- .../ui/activity/FileDisplayActivity.kt | 20 +++++- .../android/ui/adapter/OCFileListAdapter.java | 35 ++++++++++ .../android/ui/adapter/OCFileListDelegate.kt | 65 +++++++++++-------- 7 files changed, 99 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt index 86d02064c7a3..13dd20a4efe6 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt @@ -99,7 +99,7 @@ interface FileDao { ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER} """ ) - suspend fun getSubfiles( + fun getSubfiles( parentId: Long, accountName: String, dirType: String = MimeType.DIRECTORY, diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt index 76f4b99ae7b3..f3e0b68c8a61 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt @@ -47,11 +47,7 @@ class FileDownloadHelper { return false } - return if (file.isFolder) { - FolderDownloadWorker.isDownloading(file.fileId) - } else { - FileDownloadWorker.isDownloading(user.accountName, file.fileId) - } + return FileDownloadWorker.isDownloading(user.accountName, file.fileId) } fun cancelPendingOrCurrentDownloads(user: User?, files: List?) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt index 337ff1bf6ee7..52e06e14ebbb 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt @@ -24,8 +24,9 @@ import com.owncloud.android.operations.DownloadType import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext -import java.util.concurrent.ConcurrentHashMap @Suppress("LongMethod", "TooGenericExceptionCaught") class FolderDownloadWorker( @@ -40,9 +41,8 @@ class FolderDownloadWorker( const val FOLDER_ID = "FOLDER_ID" const val ACCOUNT_NAME = "ACCOUNT_NAME" - private val pendingDownloads: MutableSet = ConcurrentHashMap.newKeySet() - - fun isDownloading(id: Long): Boolean = pendingDownloads.contains(id) + private val _activeFolders = MutableStateFlow>(emptySet()) + val activeFolders: StateFlow> = _activeFolders } private val notificationManager = FolderDownloadWorkerNotificationManager(context, viewThemeUtils) @@ -79,7 +79,7 @@ class FolderDownloadWorker( trySetForeground(folder) - pendingDownloads.add(folder.fileId) + _activeFolders.value += folderID val downloadHelper = FileDownloadHelper.instance() @@ -137,7 +137,7 @@ class FolderDownloadWorker( Result.failure() } finally { WorkerStateObserver.send(WorkerState.FolderDownloadCompleted(folder)) - pendingDownloads.remove(folder.fileId) + _activeFolders.value -= folderID notificationManager.dismiss() } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt index 77f662d76c11..6567d7c5b559 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt @@ -44,7 +44,7 @@ fun FileDataStorageManager.getDecryptedPath(file: OCFile): String { .joinToString(OCFile.PATH_SEPARATOR) } -suspend fun FileDataStorageManager.getSubfiles(id: Long, accountName: String): List = +fun FileDataStorageManager.getSubfiles(id: Long, accountName: String): List = fileDao.getSubfiles(id, accountName).map { createFileInstance(it) } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 651f4741639d..b10643034e5d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -47,7 +47,9 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.MenuItemCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.google.android.material.appbar.AppBarLayout import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -65,6 +67,7 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper import com.nextcloud.client.jobs.download.FileDownloadWorker import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadAddedMessage import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage +import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker @@ -76,6 +79,7 @@ import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerState.FileDownloadCompleted import com.nextcloud.model.WorkerState.FileDownloadStarted import com.nextcloud.model.WorkerState.OfflineOperationsCompleted +import com.nextcloud.model.WorkerStateObserver import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isActive import com.nextcloud.utils.extensions.lastFragment @@ -279,6 +283,7 @@ class FileDisplayActivity : startMetadataSyncForRoot() handleBackPress() setupDrawer(menuItemId) + observeFolderDownloadWorker() } /** @@ -2411,10 +2416,21 @@ class FileDisplayActivity : } } + private fun observeFolderDownloadWorker() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + FolderDownloadWorker.activeFolders.collect { activeIds -> + Log_OC.d(TAG, "currently downloading: $activeIds") + listOfFilesFragment?.adapter?.notifyDownloadingFolderIds(activeIds) + } + } + } + } + private fun requestForDownload(file: OCFile, downloadBehaviour: String, packageName: String, activityName: String) { val currentUser = user.orElseThrow(Supplier { RuntimeException() }) - if (!FileDownloadHelper.Companion.instance().isDownloading(currentUser, file)) { - FileDownloadHelper.Companion.instance().downloadFile( + if (!FileDownloadHelper.instance().isDownloading(currentUser, file)) { + FileDownloadHelper.instance().downloadFile( currentUser, file, downloadBehaviour, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index c93624ba352c..731c4f4d0890 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -75,6 +75,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; @@ -1061,4 +1062,38 @@ public void removeAllFiles() { mFilesAll.clear(); notifyDataSetChanged(); } + + // In OCFileListAdapter.java + public void notifyDownloadingFolderIds(Set ids) { + // Get the previous downloading IDs before updating + Set previousIds = new HashSet<>(ocFileListDelegate.getDownloadingFolderIds()); + + // Update the delegate + ocFileListDelegate.notifyDownloadingFolderIds(ids); + + // Find IDs that changed (added OR removed) + Set allChangedIds = new HashSet<>(); + allChangedIds.addAll(ids); // New downloads + allChangedIds.addAll(previousIds); // Stopped downloads + + // Notify all affected items + allChangedIds.forEach(id -> { + OCFile file = findOCFile(id); + if (file != null) { + int position = mFiles.indexOf(file); + if (position != -1) { + notifyItemChanged(position); + } + } + }); + } + + private OCFile findOCFile(long id) { + for (OCFile file : mFiles) { + if (file.getFileId() == id) { + return file; + } + } + return null; + } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index 343659e8938a..78da94df38c8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -43,7 +43,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @Suppress("LongParameterList", "TooManyFunctions") class OCFileListDelegate( @@ -68,6 +67,7 @@ class OCFileListDelegate( private val asyncTasks: MutableList = ArrayList() private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val galleryImageGenerationJob = GalleryImageGenerationJob(user, storageManager) + private val downloadingFolderIds = mutableSetOf() fun setHighlightedItem(highlightedItem: OCFile?) { this.highlightedItem = highlightedItem @@ -323,45 +323,42 @@ class OCFileListDelegate( } } - private suspend fun isFolderFullyDownloaded(file: OCFile): Boolean = withContext(Dispatchers.IO) { - file.isFolder && - storageManager.getSubfiles(file.fileId, user.accountName) - .takeIf { it.isNotEmpty() } - ?.all { it.isDown } == true - } + private fun isFolderFullyDownloaded(file: OCFile): Boolean = file.isFolder && + storageManager.getSubfiles(file.fileId, user.accountName) + .takeIf { it.isNotEmpty() } + ?.all { it.isDown } == true private fun isSynchronizing(file: OCFile): Boolean { val operationsServiceBinder = transferServiceGetter.operationsServiceBinder val fileDownloadHelper = FileDownloadHelper.instance() + Log_OC.d(TAG, "size of downloading folder: " + downloadingFolderIds.size) + return operationsServiceBinder?.isSynchronizing(user, file) == true || fileDownloadHelper.isDownloading(user, file) || + downloadingFolderIds.contains(file.fileId) || fileUploadHelper.isUploading(file.remotePath, user.accountName) } private fun showLocalFileIndicator(file: OCFile, holder: ListViewHolder) { - ioScope.launch { - val isFullyDownloaded = isFolderFullyDownloaded(file) - val isSyncing = isSynchronizing(file) - val hasConflict = (file.etagInConflict != null) - val isDown = file.isDown - - val icon = when { - isSyncing -> R.drawable.ic_synchronizing - hasConflict -> R.drawable.ic_synchronizing_error - isDown || isFullyDownloaded -> R.drawable.ic_synced - else -> null - } + val isFullyDownloaded = isFolderFullyDownloaded(file) + val isSyncing = isSynchronizing(file) + val hasConflict = (file.etagInConflict != null) + val isDown = file.isDown + + val icon = when { + isSyncing -> R.drawable.ic_synchronizing + hasConflict -> R.drawable.ic_synchronizing_error + isDown || isFullyDownloaded -> R.drawable.ic_synced + else -> null + } - withContext(Dispatchers.Main) { - holder.localFileIndicator.run { - if (icon != null && showMetadata) { - setImageResource(icon) - visibility = View.VISIBLE - } else { - visibility = View.GONE - } - } + holder.localFileIndicator.run { + if (icon != null && showMetadata) { + setImageResource(icon) + visibility = View.VISIBLE + } else { + visibility = View.GONE } } } @@ -418,6 +415,18 @@ class OCFileListDelegate( showShareAvatar = bool } + fun notifyDownloadingFolderIds(ids: Set) { + val removed = downloadingFolderIds - ids + val added = ids - downloadingFolderIds + + downloadingFolderIds.clear() + downloadingFolderIds.addAll(ids) + + Log_OC.d(TAG, "downloading folders - added: $added, removed: $removed, current: $downloadingFolderIds") + } + + fun getDownloadingFolderIds(): List = downloadingFolderIds.toList() + fun cleanup() { ioScope.cancel() From ac9d6c6a42f83ca89092202ac5a26101bc19f02a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 13:33:11 +0100 Subject: [PATCH 2/6] fix(file-list): sync icon appearance Signed-off-by: alperozturk96 --- .../owncloud/android/ui/adapter/OCFileListAdapter.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 731c4f4d0890..748aa7cef256 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -1063,20 +1063,15 @@ public void removeAllFiles() { notifyDataSetChanged(); } - // In OCFileListAdapter.java public void notifyDownloadingFolderIds(Set ids) { - // Get the previous downloading IDs before updating Set previousIds = new HashSet<>(ocFileListDelegate.getDownloadingFolderIds()); - // Update the delegate ocFileListDelegate.notifyDownloadingFolderIds(ids); - // Find IDs that changed (added OR removed) Set allChangedIds = new HashSet<>(); - allChangedIds.addAll(ids); // New downloads - allChangedIds.addAll(previousIds); // Stopped downloads + allChangedIds.addAll(ids); + allChangedIds.addAll(previousIds); - // Notify all affected items allChangedIds.forEach(id -> { OCFile file = findOCFile(id); if (file != null) { From 13784974633ee50ac00af53ef4d524f6828b7f57 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 15:21:15 +0100 Subject: [PATCH 3/6] fix(file-list): sync icon disappearance Signed-off-by: alperozturk96 --- .../folderDownload/FolderDownloadState.kt | 13 +++++++++ .../folderDownload/FolderDownloadWorker.kt | 8 +++--- .../ui/activity/FileDisplayActivity.kt | 13 ++++++--- .../android/ui/adapter/OCFileListAdapter.java | 20 +++++++------ .../android/ui/adapter/OCFileListDelegate.kt | 28 +++++++++++-------- 5 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadState.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadState.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadState.kt new file mode 100644 index 000000000000..c4553f5a24d8 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadState.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.folderDownload + +sealed class FolderDownloadState(open val id: Long) { + data class Downloading(override val id: Long) : FolderDownloadState(id) + data class Removed(override val id: Long) : FolderDownloadState(id) +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt index 52e06e14ebbb..e5d9e95030a4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt @@ -41,8 +41,8 @@ class FolderDownloadWorker( const val FOLDER_ID = "FOLDER_ID" const val ACCOUNT_NAME = "ACCOUNT_NAME" - private val _activeFolders = MutableStateFlow>(emptySet()) - val activeFolders: StateFlow> = _activeFolders + private val _activeFolders = MutableStateFlow>(emptySet()) + val activeFolders: StateFlow> = _activeFolders } private val notificationManager = FolderDownloadWorkerNotificationManager(context, viewThemeUtils) @@ -79,7 +79,7 @@ class FolderDownloadWorker( trySetForeground(folder) - _activeFolders.value += folderID + _activeFolders.value += FolderDownloadState.Downloading(folderID) val downloadHelper = FileDownloadHelper.instance() @@ -137,7 +137,7 @@ class FolderDownloadWorker( Result.failure() } finally { WorkerStateObserver.send(WorkerState.FolderDownloadCompleted(folder)) - _activeFolders.value -= folderID + _activeFolders.value -= FolderDownloadState.Downloading(folderID) notificationManager.dismiss() } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index b10643034e5d..3f4d1817af3c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -67,6 +67,7 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper import com.nextcloud.client.jobs.download.FileDownloadWorker import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadAddedMessage import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage +import com.nextcloud.client.jobs.folderDownload.FolderDownloadState import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadHelper @@ -79,7 +80,6 @@ import com.nextcloud.model.WorkerState import com.nextcloud.model.WorkerState.FileDownloadCompleted import com.nextcloud.model.WorkerState.FileDownloadStarted import com.nextcloud.model.WorkerState.OfflineOperationsCompleted -import com.nextcloud.model.WorkerStateObserver import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isActive import com.nextcloud.utils.extensions.lastFragment @@ -2135,6 +2135,11 @@ class FileDisplayActivity : } supportInvalidateOptionsMenu() fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) + + if (removedFile.isFolder) { + val deletedFolderDownloadState = FolderDownloadState.Removed(removedFile.fileId) + listOfFilesFragment?.adapter?.notifyFolderDownloadStates(setOf(deletedFolderDownloadState)) + } } else { if (result.isSslRecoverableException) { mLastSslUntrustedServerResult = result @@ -2419,9 +2424,9 @@ class FileDisplayActivity : private fun observeFolderDownloadWorker() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - FolderDownloadWorker.activeFolders.collect { activeIds -> - Log_OC.d(TAG, "currently downloading: $activeIds") - listOfFilesFragment?.adapter?.notifyDownloadingFolderIds(activeIds) + FolderDownloadWorker.activeFolders.collect { workerStates -> + Log_OC.d(TAG, "currently downloading: ${workerStates.size}") + listOfFilesFragment?.adapter?.notifyFolderDownloadStates(workerStates) } } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 748aa7cef256..d88cc687af8a 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -30,6 +30,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.database.entity.OfflineOperationEntity; +import com.nextcloud.client.jobs.folderDownload.FolderDownloadState; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.model.OfflineOperationType; @@ -1063,19 +1064,20 @@ public void removeAllFiles() { notifyDataSetChanged(); } - public void notifyDownloadingFolderIds(Set ids) { - Set previousIds = new HashSet<>(ocFileListDelegate.getDownloadingFolderIds()); + public void notifyFolderDownloadStates(Set states) { + Set previousStates = new HashSet<>(ocFileListDelegate.getFolderDownloadStates()); - ocFileListDelegate.notifyDownloadingFolderIds(ids); + ocFileListDelegate.addFolderDownloadStates(states); - Set allChangedIds = new HashSet<>(); - allChangedIds.addAll(ids); - allChangedIds.addAll(previousIds); + Set changedFileIds = new HashSet<>(); + states.forEach(state -> changedFileIds.add(state.getId())); + previousStates.forEach(state -> changedFileIds.add(state.getId())); - allChangedIds.forEach(id -> { - OCFile file = findOCFile(id); + + changedFileIds.forEach(fileId -> { + OCFile file = findOCFile(fileId); if (file != null) { - int position = mFiles.indexOf(file); + int position = getItemPosition(file); if (position != -1) { notifyItemChanged(position); } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index 78da94df38c8..75e77a2ec199 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -15,6 +15,7 @@ import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.jobs.folderDownload.FolderDownloadState import com.nextcloud.client.jobs.gallery.GalleryImageGenerationJob import com.nextcloud.client.jobs.gallery.GalleryImageGenerationListener import com.nextcloud.client.jobs.upload.FileUploadHelper @@ -67,7 +68,7 @@ class OCFileListDelegate( private val asyncTasks: MutableList = ArrayList() private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val galleryImageGenerationJob = GalleryImageGenerationJob(user, storageManager) - private val downloadingFolderIds = mutableSetOf() + private val folderDownloadStates = mutableSetOf() fun setHighlightedItem(highlightedItem: OCFile?) { this.highlightedItem = highlightedItem @@ -331,12 +332,13 @@ class OCFileListDelegate( private fun isSynchronizing(file: OCFile): Boolean { val operationsServiceBinder = transferServiceGetter.operationsServiceBinder val fileDownloadHelper = FileDownloadHelper.instance() + val isDownloadingFolder = + folderDownloadStates.any { it is FolderDownloadState.Downloading && it.id == file.fileId } - Log_OC.d(TAG, "size of downloading folder: " + downloadingFolderIds.size) - + Log_OC.d(TAG, "size of downloading folder: " + folderDownloadStates.size) return operationsServiceBinder?.isSynchronizing(user, file) == true || fileDownloadHelper.isDownloading(user, file) || - downloadingFolderIds.contains(file.fileId) || + isDownloadingFolder || fileUploadHelper.isUploading(file.remotePath, user.accountName) } @@ -345,11 +347,15 @@ class OCFileListDelegate( val isSyncing = isSynchronizing(file) val hasConflict = (file.etagInConflict != null) val isDown = file.isDown + val isRemoved = folderDownloadStates.any { + it is FolderDownloadState.Removed && it.id == file.fileId + } val icon = when { isSyncing -> R.drawable.ic_synchronizing hasConflict -> R.drawable.ic_synchronizing_error isDown || isFullyDownloaded -> R.drawable.ic_synced + isRemoved -> null else -> null } @@ -415,17 +421,17 @@ class OCFileListDelegate( showShareAvatar = bool } - fun notifyDownloadingFolderIds(ids: Set) { - val removed = downloadingFolderIds - ids - val added = ids - downloadingFolderIds + fun addFolderDownloadStates(ids: Set) { + val removed = folderDownloadStates - ids + val added = ids - folderDownloadStates - downloadingFolderIds.clear() - downloadingFolderIds.addAll(ids) + folderDownloadStates.clear() + folderDownloadStates.addAll(ids) - Log_OC.d(TAG, "downloading folders - added: $added, removed: $removed, current: $downloadingFolderIds") + Log_OC.d(TAG, "downloading folders - added: $added, removed: $removed, current: $folderDownloadStates") } - fun getDownloadingFolderIds(): List = downloadingFolderIds.toList() + fun getFolderDownloadStates(): List = folderDownloadStates.toList() fun cleanup() { ioScope.cancel() From 1c147f0a1caf9cc5917c123f688c0f5ef3504677 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 15:38:42 +0100 Subject: [PATCH 4/6] fix(file-list): cleanup states Signed-off-by: alperozturk96 --- .../owncloud/android/ui/adapter/OCFileListAdapter.java | 1 + .../com/owncloud/android/ui/adapter/OCFileListDelegate.kt | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index d88cc687af8a..41a3cc50ee55 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -871,6 +871,7 @@ public void updateAdapter(List newFiles, OCFile directory) { mFiles = new ArrayList<>(newFiles); mFilesAll.clear(); + ocFileListDelegate.clearFolderDownloadStates(); mFilesAll.addAll(mFiles); if (directory != null) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index 75e77a2ec199..c7dde3a7ca55 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -422,17 +422,17 @@ class OCFileListDelegate( } fun addFolderDownloadStates(ids: Set) { - val removed = folderDownloadStates - ids - val added = ids - folderDownloadStates - folderDownloadStates.clear() folderDownloadStates.addAll(ids) - Log_OC.d(TAG, "downloading folders - added: $added, removed: $removed, current: $folderDownloadStates") + Log_OC.d(TAG, "downloading folders - added current: $folderDownloadStates") } fun getFolderDownloadStates(): List = folderDownloadStates.toList() + // only clear remove states since downloading states added and removed via worker + fun clearFolderDownloadStates() = folderDownloadStates.removeIf { it is FolderDownloadState.Removed } + fun cleanup() { ioScope.cancel() From 664d92d97f3c75db0e76182ce26ce1269f66103e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 15:39:39 +0100 Subject: [PATCH 5/6] fix(file-list): cleanup states Signed-off-by: alperozturk96 --- .../com/nextcloud/client/jobs/download/FileDownloadHelper.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt index f3e0b68c8a61..6fbe1e7ac151 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt @@ -9,7 +9,6 @@ package com.nextcloud.client.jobs.download import com.nextcloud.client.account.User import com.nextcloud.client.jobs.BackgroundJobManager -import com.nextcloud.client.jobs.folderDownload.FolderDownloadWorker import com.owncloud.android.MainApp import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile From 20e0802a2c07e9ed697ea9bafa055c0abeadbe22 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 9 Feb 2026 15:42:23 +0100 Subject: [PATCH 6/6] fix(file-list): cleanup states Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/ui/adapter/OCFileListAdapter.java | 1 - .../java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 41a3cc50ee55..02b6966f4513 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -1074,7 +1074,6 @@ public void notifyFolderDownloadStates(Set states) { states.forEach(state -> changedFileIds.add(state.getId())); previousStates.forEach(state -> changedFileIds.add(state.getId())); - changedFileIds.forEach(fileId -> { OCFile file = findOCFile(fileId); if (file != null) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index c7dde3a7ca55..07a1d6dc649c 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -335,7 +335,6 @@ class OCFileListDelegate( val isDownloadingFolder = folderDownloadStates.any { it is FolderDownloadState.Downloading && it.id == file.fileId } - Log_OC.d(TAG, "size of downloading folder: " + folderDownloadStates.size) return operationsServiceBinder?.isSynchronizing(user, file) == true || fileDownloadHelper.isDownloading(user, file) || isDownloadingFolder ||