From 2b54423ac7fa065aeff68d2f0bd8a63720ef327a Mon Sep 17 00:00:00 2001 From: mykh-hailo Date: Tue, 10 Mar 2026 23:24:49 -0400 Subject: [PATCH 1/3] feat: show destination folder snackbar for copy/move operations --- .../android/extensions/ActivityExt.kt | 12 +++++++ .../ui/activity/FileDisplayActivity.kt | 31 +++++++++++++++++++ owncloudApp/src/main/res/values/strings.xml | 3 ++ 3 files changed, 46 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ActivityExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ActivityExt.kt index c1efbf2966c..a7991ce8671 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/ActivityExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ActivityExt.kt @@ -90,6 +90,18 @@ fun Activity.showMessageInSnackbar( Snackbar.make(findViewById(layoutId), message, duration).show() } +fun Activity.showSnackbarWithAction( + message: CharSequence, + action: () -> Unit, + actionText: CharSequence, + duration: Int = Snackbar.LENGTH_LONG, + layoutId: Int = android.R.id.content +) { + Snackbar.make(findViewById(layoutId), message, duration) + .setAction(actionText) { action() } + .show() +} + fun Activity.showErrorInToast( genericErrorMessageId: Int, throwable: Throwable?, diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index c849947a10b..74da73d85fe 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -84,6 +84,7 @@ import com.owncloud.android.extensions.parseError import com.owncloud.android.extensions.sendDownloadedFilesByShareSheet import com.owncloud.android.extensions.showErrorInSnackbar import com.owncloud.android.extensions.showMessageInSnackbar +import com.owncloud.android.extensions.showSnackbarWithAction import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.lib.common.authentication.OwnCloudBearerCredentials import com.owncloud.android.lib.common.network.CertificateCombinedException @@ -181,6 +182,8 @@ class FileDisplayActivity : FileActivity(), private var waitingToSend: OCFile? = null private var waitingToOpen: OCFile? = null + private var copyMoveTargetFolder: OCFile? = null + private var localBroadcastManager: LocalBroadcastManager? = null private val fileOperationsViewModel: FileOperationsViewModel by viewModel() @@ -707,6 +710,7 @@ class FileDisplayActivity : FileActivity(), private fun requestMoveOperation(data: Intent) { val folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER) ?: return val files = data.getParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES) ?: return + copyMoveTargetFolder = folderToMoveAt val moveOperation = FileOperation.MoveOperation( listOfFilesToMove = files.toList(), targetFolder = folderToMoveAt, @@ -723,6 +727,7 @@ class FileDisplayActivity : FileActivity(), private fun requestCopyOperation(data: Intent) { val folderToCopyAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER) ?: return val files = data.getParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES) ?: return + copyMoveTargetFolder = folderToCopyAt val copyOperation = FileOperation.CopyOperation( listOfFilesToCopy = files.toList(), targetFolder = folderToCopyAt, @@ -1082,6 +1087,9 @@ class FileDisplayActivity : FileActivity(), // Refresh the spaces and update the quota spacesListViewModel.refreshSpacesFromServer() } + if (uiResult.data.isNullOrEmpty()) { + showCopyMoveSuccessSnackbar(isCopy = false) + } } is UIResult.Error -> { @@ -1130,6 +1138,9 @@ class FileDisplayActivity : FileActivity(), // Refresh the spaces and update the quota spacesListViewModel.refreshSpacesFromServer() + if (uiResult.data.isNullOrEmpty()) { + showCopyMoveSuccessSnackbar(isCopy = true) + } } is UIResult.Error -> { @@ -1148,6 +1159,26 @@ class FileDisplayActivity : FileActivity(), } } + private fun showCopyMoveSuccessSnackbar(isCopy: Boolean) { + val message = getString(if (isCopy) R.string.copy_success_msg else R.string.move_success_msg) + val targetFolderId = copyMoveTargetFolder?.id + if (targetFolderId != null) { + showSnackbarWithAction( + message = message, + actionText = getString(R.string.go_to_destination_folder), + action = { + val fileListFragment = mainFileListFragment + ?: supportFragmentManager.findFragmentById(R.id.left_fragment_container) as? MainFileListFragment + fileListFragment?.navigateToFolderId(targetFolderId) + }, + layoutId = R.id.list_layout + ) + } else { + showMessageInSnackbar(R.id.list_layout, message) + } + copyMoveTargetFolder = null + } + private fun showConflictDecisionDialog( uiResult: UIResult.Success>, data: List, diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index b3ed3673f45..cea63b9b764 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -551,6 +551,9 @@ It is not possible to copy a folder into a descendant. The file exists already in the destination folder. An error occurred while trying to copy this file or folder. + The file has been copied + The file has been moved + Go to target folder to copy this file Camera uploads From 6c24b478f7e228720282a91c1dacb6053b7a9deb Mon Sep 17 00:00:00 2001 From: mykh-hailo Date: Tue, 10 Mar 2026 23:25:28 -0400 Subject: [PATCH 2/3] fix: hide three-dot menu in folder picker mode --- .../android/presentation/files/filelist/FileListAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt index 5e8bb13ddff..fd0cf999b3f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt @@ -288,7 +288,7 @@ class FileListAdapter( params -> params.marginStart = if (isFolderInKw) 0 else context.resources.getDimensionPixelSize(R.dimen.standard_quarter_margin) } it.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp) - it.threeDotMenu.isVisible = getCheckedItems().isEmpty() + it.threeDotMenu.isVisible = !isPickerMode && getCheckedItems().isEmpty() it.threeDotMenu.contentDescription = context.getString(R.string.content_description_file_operations, file.fileName) if (fileListOption.isAvailableOffline() || (fileListOption.isSharedByLink() && fileWithSyncInfo.space == null)) { it.spacePathLine.path.apply { From 9a416e0ccc635b37ee72cff6b95bd2e023c45f76 Mon Sep 17 00:00:00 2001 From: mykh-hailo Date: Tue, 10 Mar 2026 23:45:53 -0400 Subject: [PATCH 3/3] chore: add changelog --- changelog/unreleased/4802 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/4802 diff --git a/changelog/unreleased/4802 b/changelog/unreleased/4802 new file mode 100644 index 00000000000..7cb096d68a4 --- /dev/null +++ b/changelog/unreleased/4802 @@ -0,0 +1,6 @@ +Enhancement: Show destination folder snackbar for copy/move operations + +A snackbar message has been displayed after copy or move operations with an action button that allows users to quickly navigate to the destination folder. + +https://github.com/owncloud/android/issues/4379 +https://github.com/owncloud/android/pull/4802