From 7114f33afc5dc822c46d79f747c88757443ff178 Mon Sep 17 00:00:00 2001 From: Philipp Hasper Date: Sat, 8 Nov 2025 09:34:36 +0100 Subject: [PATCH 1/4] Move or Copy files: Start in the parent folder instead of root Previous behavior always moved up to the root folder, which meant the user lost the context of their current files. Especially in complicated nested folder structures, this made standard operations overly complicated, like duplicating in the same folder or duplicating/moving to a sibling folder. This is now also in sync with the behavior of the iOS app and the web UI Signed-off-by: Philipp Hasper --- .../android/ui/activity/FolderPickerActivity.kt | 14 ++++++++++---- .../android/ui/fragment/OCFileListFragment.java | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 3730796b21ae..52e137f184c2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.lifecycleScope import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.fileNameValidator.FileNameValidator import com.owncloud.android.R import com.owncloud.android.databinding.FilesFolderPickerBinding @@ -275,8 +276,13 @@ open class FolderPickerActivity : super.onResume() Log_OC.e(TAG, "onResume() start") - refreshListOfFilesFragment(false) - file = listOfFilesFragment?.currentFile + val extraFolder = intent.getParcelableArgument(EXTRA_FOLDER.toString(), OCFile::class.java) + if (extraFolder != null) { + file = extraFolder + } else { + file = listOfFilesFragment?.currentFile + } + refreshListOfFilesFragment(file, false) updateUiElements() val intentFilter = getSyncIntentFilter() @@ -351,8 +357,8 @@ open class FolderPickerActivity : } } - private fun refreshListOfFilesFragment(fromSearch: Boolean) { - listOfFilesFragment?.listDirectory(false, fromSearch) + private fun refreshListOfFilesFragment(directory: OCFile, fromSearch: Boolean) { + listOfFilesFragment?.listDirectory(directory, false, fromSearch) } fun browseToRoot() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 18038398955f..25f7a9b9d8de 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -1437,6 +1437,8 @@ private void pickFolderForMoveOrCopy(final Set checkedFiles) { paths.add(file.getRemotePath()); } action.putStringArrayListExtra(FolderPickerActivity.EXTRA_FILE_PATHS, paths); + action.putExtra(FolderPickerActivity.EXTRA_FOLDER, getCurrentFile()); + action.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); // No animation since we stay in the same folder action.putExtra(FolderPickerActivity.EXTRA_ACTION, extraAction); getActivity().startActivityForResult(action, requestCode); } From 2b88c8f284af871e44845a30ab34427bbbc4b64c Mon Sep 17 00:00:00 2001 From: Philipp Hasper Date: Sat, 8 Nov 2025 09:35:14 +0100 Subject: [PATCH 2/4] OCFileListFragment: Removed unused `fromSearch` arg of listDirectory() Last usage of the argument inside of the method was removed in 905222c930, so removing it doesn't have any impact on the behavior. The only instance where the parameter was set to `true` was in the ExtendedListFragment Signed-off-by: Philipp Hasper --- .../OCFileListFragmentStaticServerIT.kt | 8 ++--- .../ui/activity/FileDisplayActivity.kt | 30 +++++++++---------- .../ui/activity/FolderPickerActivity.kt | 12 ++++---- .../ui/fragment/ExtendedListFragment.kt | 2 +- .../ui/fragment/OCFileListFragment.java | 22 +++++++------- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt index 628c28188e4b..9b908f53a860 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentStaticServerIT.kt @@ -89,7 +89,7 @@ class OCFileListFragmentStaticServerIT : AbstractIT() { sut.supportFragmentManager.executePendingTransactions() val fragment = (sut.fragment as OCFileListFragment) val root = sut.storageManager.getFileByEncryptedRemotePath("/") - fragment.listDirectory(root, false, false) + fragment.listDirectory(root, false) } val screenShotName = createName(testClassName + "_" + "showFiles", "") @@ -220,7 +220,7 @@ class OCFileListFragmentStaticServerIT : AbstractIT() { sut.addFragment(fragment) sut.supportFragmentManager.executePendingTransactions() val root = sut.storageManager.getFileByEncryptedRemotePath("/") - fragment.listDirectory(root, false, false) + fragment.listDirectory(root, false) fragment.adapter.setShowShareAvatar(true) } @@ -308,7 +308,7 @@ class OCFileListFragmentStaticServerIT : AbstractIT() { sut.addFragment(fragment) sut.supportFragmentManager.executePendingTransactions() val root = sut.storageManager.getFileByEncryptedRemotePath("/") - fragment.listDirectory(root, false, false) + fragment.listDirectory(root, false) fragment.adapter.setShowShareAvatar(true) } @@ -347,7 +347,7 @@ class OCFileListFragmentStaticServerIT : AbstractIT() { sut.supportFragmentManager.executePendingTransactions() val testFolder: OCFile = sut.storageManager.getFileByEncryptedRemotePath("/test/") testFolder.richWorkspace = getFile("java.md").readText() - fragment.listDirectory(testFolder, false, false) + fragment.listDirectory(testFolder, false) } val screenShotName = createName(testClassName + "_" + "showRichWorkspace", "") 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 ff5e800637b8..a99edf52417e 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 @@ -531,7 +531,7 @@ class FileDisplayActivity : /** First fragment */ val listOfFiles = this.listOfFilesFragment if (listOfFiles != null && TextUtils.isEmpty(searchQuery)) { - listOfFiles.listDirectory(getCurrentDir(), file, MainApp.isOnlyOnDevice(), false) + listOfFiles.listDirectory(getCurrentDir(), file, MainApp.isOnlyOnDevice()) } else { Log_OC.e(TAG, "Still have a chance to lose the initialization of list fragment >(") } @@ -806,9 +806,9 @@ class FileDisplayActivity : resetScrolling(true) } - fun updateListOfFilesFragment(fromSearch: Boolean) { + fun updateListOfFilesFragment() { val fileListFragment = this.listOfFilesFragment - fileListFragment?.listDirectory(MainApp.isOnlyOnDevice(), fromSearch) + fileListFragment?.listDirectory(MainApp.isOnlyOnDevice()) } fun resetSearchView() { @@ -1357,10 +1357,10 @@ class FileDisplayActivity : if (searchView != null && !TextUtils.isEmpty(searchQuery)) { searchView?.setQuery(searchQuery, false) } else if (!ocFileListFragment.isSearchFragment && startFile == null) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() ocFileListFragment.registerFabListener() } else { - ocFileListFragment.listDirectory(startFile, false, false) + ocFileListFragment.listDirectory(startFile, false) updateActionBarTitleAndHomeButton(startFile) } @@ -1565,7 +1565,7 @@ class FileDisplayActivity : return } - ocFileListFragment.listDirectory(currentDir, MainApp.isOnlyOnDevice(), false) + ocFileListFragment.listDirectory(currentDir, MainApp.isOnlyOnDevice()) } private fun handleScrollBehaviour(ocFileListFragment: OCFileListFragment?) { @@ -1679,7 +1679,7 @@ class FileDisplayActivity : if (sameAccount && isDescendant) { val linkedToRemotePath = intent.getStringExtra(FileUploadWorker.EXTRA_LINKED_TO_PATH) if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() } } @@ -1752,7 +1752,7 @@ class FileDisplayActivity : if (sameAccount && isDescendant) { val linkedToRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_LINKED_TO_PATH) if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() } val intentAction = intent.action @@ -1813,7 +1813,7 @@ class FileDisplayActivity : val listOfFiles = this.listOfFilesFragment if (listOfFiles != null) { // should never be null, indeed val root = storageManager.getFileByPath(OCFile.ROOT_PATH) - listOfFiles.listDirectory(root, MainApp.isOnlyOnDevice(), false) + listOfFiles.listDirectory(root, MainApp.isOnlyOnDevice()) file = listOfFiles.currentFile startSyncFolderOperation(root, false) } @@ -1998,7 +1998,7 @@ class FileDisplayActivity : if (fileListFragment == null) { fileListFragment = listOfFilesFragment } - fileListFragment?.listDirectory(currentDir, MainApp.isOnlyOnDevice(), false) + fileListFragment?.listDirectory(currentDir, MainApp.isOnlyOnDevice()) } private fun handleDownloadWorkerState() { @@ -2124,7 +2124,7 @@ class FileDisplayActivity : } val parentFile = storageManager.getFileById(removedFile.parentId) if (parentFile != null && parentFile == getCurrentDir()) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() } else if (leftFragment is OCFileListFragment && SearchRemoteOperation.SearchType.FAVORITE_SEARCH == leftFragment.searchEvent?.searchType ) { @@ -2208,7 +2208,7 @@ class FileDisplayActivity : */ private fun onCopyFileOperationFinish(operation: CopyFileOperation?, result: RemoteOperationResult<*>) { if (result.isSuccess) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() refreshGalleryFragmentIfNeeded() } else { try { @@ -2258,7 +2258,7 @@ class FileDisplayActivity : val file = storageManager.getFileById(renamedFile.parentId) if (file != null && file == getCurrentDir()) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() } refreshGalleryFragmentIfNeeded() fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) @@ -2317,7 +2317,7 @@ class FileDisplayActivity : * {@inheritDoc} */ override fun onTransferStateChanged(file: OCFile, downloading: Boolean, uploading: Boolean) { - updateListOfFilesFragment(false) + updateListOfFilesFragment() val leftFragment = this.leftFragment val optionalUser = user if (leftFragment is FileDetailFragment && file == leftFragment.file && optionalUser.isPresent) { @@ -3050,7 +3050,7 @@ class FileDisplayActivity : if (TextUtils.isEmpty(message)) { val temp = file file = getCurrentDir() - listOfFiles.listDirectory(getCurrentDir(), temp, MainApp.isOnlyOnDevice(), false) + listOfFiles.listDirectory(getCurrentDir(), temp, MainApp.isOnlyOnDevice()) updateActionBarTitleAndHomeButton(null) } else { val view = listOfFiles.view diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 52e137f184c2..af34ba30f4ec 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -189,7 +189,7 @@ open class FolderPickerActivity : folder = file } - listOfFilesFragment?.listDirectory(folder, false, false) + listOfFilesFragment?.listDirectory(folder, false) startSyncFolderOperation(folder, false) updateUiElements() } @@ -282,7 +282,7 @@ open class FolderPickerActivity : } else { file = listOfFilesFragment?.currentFile } - refreshListOfFilesFragment(file, false) + refreshListOfFilesFragment(file) updateUiElements() val intentFilter = getSyncIntentFilter() @@ -357,14 +357,14 @@ open class FolderPickerActivity : } } - private fun refreshListOfFilesFragment(directory: OCFile, fromSearch: Boolean) { - listOfFilesFragment?.listDirectory(directory, false, fromSearch) + private fun refreshListOfFilesFragment(directory: OCFile) { + listOfFilesFragment?.listDirectory(directory, false) } fun browseToRoot() { listOfFilesFragment?.let { val root = storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH) - it.listDirectory(root, false, false) + it.listDirectory(root, false) file = it.currentFile updateUiElements() startSyncFolderOperation(root, false) @@ -566,7 +566,7 @@ open class FolderPickerActivity : currentFile = currentDir } if (currentDir.remotePath == syncFolderRemotePath) { - listOfFilesFragment?.listDirectory(currentDir, false, false) + listOfFilesFragment?.listDirectory(currentDir, false) } file = currentFile } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt index 54bbad82e7ab..a7c5aae95e72 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt @@ -254,7 +254,7 @@ open class ExtendedListFragment : is FileDisplayActivity -> { if (isBackPressed && query.isEmpty()) { activity.resetSearchView() - activity.updateListOfFilesFragment(true) + activity.updateListOfFilesFragment() } else { Handler(Looper.getMainLooper()).post { if (adapter is OCFileListAdapter) { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 25f7a9b9d8de..c4af05ad20a9 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -440,7 +440,7 @@ public void onActivityCreated(Bundle savedInstanceState) { if (getActivity() instanceof FileDisplayActivity fda) { fda.updateActionBarTitleAndHomeButton(fda.getCurrentDir()); } - listDirectory(MainApp.isOnlyOnDevice(), false); + listDirectory(MainApp.isOnlyOnDevice()); } protected void setAdapter(Bundle args) { @@ -1027,7 +1027,7 @@ private String ensureTrailingSeparator(String path) { } private void updateFileList() { - listDirectory(mFile, MainApp.isOnlyOnDevice(), false); + listDirectory(mFile, MainApp.isOnlyOnDevice()); onRefresh(false); restoreIndexAndTopPosition(); } @@ -1243,7 +1243,7 @@ private void browseToFolder(OCFile file, int position) { resetMenuItems(); } - listDirectory(file, MainApp.isOnlyOnDevice(), false); + listDirectory(file, MainApp.isOnlyOnDevice()); // then, notify parent activity to let it update its state and view mContainerActivity.onBrowsedDownTo(file); // save index and top position @@ -1266,7 +1266,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { // update state and view of this fragment searchFragment = false; - listDirectory(file, MainApp.isOnlyOnDevice(), false); + listDirectory(file, MainApp.isOnlyOnDevice()); // then, notify parent activity to let it update its state and view mContainerActivity.onBrowsedDownTo(file); // save index and top position @@ -1455,10 +1455,10 @@ public OCFile getCurrentFile() { } /** - * Calls {@link OCFileListFragment#listDirectory(OCFile, boolean, boolean)} with a null parameter + * Calls {@link OCFileListFragment#listDirectory(OCFile, boolean)} with a null parameter */ - public void listDirectory(boolean onlyOnDevice, boolean fromSearch) { - listDirectory(null, onlyOnDevice, fromSearch); + public void listDirectory(boolean onlyOnDevice) { + listDirectory(null, onlyOnDevice); } public void refreshDirectory() { @@ -1470,12 +1470,12 @@ public void refreshDirectory() { final var currentFile = getCurrentFile(); if (currentFile != null) { - listDirectory(currentFile, MainApp.isOnlyOnDevice(), false); + listDirectory(currentFile, MainApp.isOnlyOnDevice()); } } - public void listDirectory(OCFile directory, boolean onlyOnDevice, boolean fromSearch) { - listDirectory(directory, null, onlyOnDevice, fromSearch); + public void listDirectory(OCFile directory, boolean onlyOnDevice) { + listDirectory(directory, null, onlyOnDevice); } private OCFile getDirectoryForListDirectory(OCFile directory, FileDataStorageManager storageManager) { @@ -1502,7 +1502,7 @@ private OCFile getDirectoryForListDirectory(OCFile directory, FileDataStorageMan * * @param directory File to be listed */ - public void listDirectory(OCFile directory, OCFile file, boolean onlyOnDevice, boolean fromSearch) { + public void listDirectory(OCFile directory, OCFile file, boolean onlyOnDevice) { if (!searchFragment) { FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); if (storageManager == null) { From f269f11103891c11e11e2049f51b9df466a61af8 Mon Sep 17 00:00:00 2001 From: Philipp Hasper Date: Thu, 11 Dec 2025 09:10:43 +0100 Subject: [PATCH 3/4] Fixing annotation for optional OCFile parameter Changing to OCFile? is required, as the parameter is indeed nullable. This didn't turn up prior to the rebase nor was it flagged in the pre-existing code, even though it is already an issue there. What probably happened is a combination of updated detection tools and the tools purposefully only flagging changed lines. Additionally, the nullness annotation was added to the underlying Java calls, to make sure the Kotlin type definition holds true. Signed-off-by: Philipp Hasper --- .../owncloud/android/ui/activity/FolderPickerActivity.kt | 2 +- .../owncloud/android/ui/fragment/OCFileListFragment.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index af34ba30f4ec..898e35461c60 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -357,7 +357,7 @@ open class FolderPickerActivity : } } - private fun refreshListOfFilesFragment(directory: OCFile) { + private fun refreshListOfFilesFragment(directory: OCFile?) { listOfFilesFragment?.listDirectory(directory, false) } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index c4af05ad20a9..b8f91ae7cb84 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -1474,11 +1474,11 @@ public void refreshDirectory() { } } - public void listDirectory(OCFile directory, boolean onlyOnDevice) { + public void listDirectory(@Nullable OCFile directory, boolean onlyOnDevice) { listDirectory(directory, null, onlyOnDevice); } - private OCFile getDirectoryForListDirectory(OCFile directory, FileDataStorageManager storageManager) { + private OCFile getDirectoryForListDirectory(@Nullable OCFile directory, FileDataStorageManager storageManager) { if (directory == null) { if (mFile != null) { directory = mFile; @@ -1502,7 +1502,7 @@ private OCFile getDirectoryForListDirectory(OCFile directory, FileDataStorageMan * * @param directory File to be listed */ - public void listDirectory(OCFile directory, OCFile file, boolean onlyOnDevice) { + public void listDirectory(@Nullable OCFile directory, OCFile file, boolean onlyOnDevice) { if (!searchFragment) { FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); if (storageManager == null) { From 385a89eb7918036fb1d2dcfc9a272b30c874cc50 Mon Sep 17 00:00:00 2001 From: Philipp Hasper Date: Tue, 16 Dec 2025 18:49:31 +0100 Subject: [PATCH 4/4] Make EXTRA_FOLDER key constant and guaranteed non-null Minimal possible change to streamline the handling of this intent key. Alternatively, the class.java.simpleName or the !! operator could've been used at time of field definition. And the same would apply to several other places as well. Signed-off-by: Philipp Hasper --- .../com/owncloud/android/ui/activity/FolderPickerActivity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 898e35461c60..106c22183bbf 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -276,7 +276,7 @@ open class FolderPickerActivity : super.onResume() Log_OC.e(TAG, "onResume() start") - val extraFolder = intent.getParcelableArgument(EXTRA_FOLDER.toString(), OCFile::class.java) + val extraFolder = intent.getParcelableArgument(EXTRA_FOLDER, OCFile::class.java) if (extraFolder != null) { file = extraFolder } else { @@ -658,8 +658,7 @@ open class FolderPickerActivity : } companion object { - @JvmField - val EXTRA_FOLDER = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_FOLDER") + const val EXTRA_FOLDER = "com.owncloud.android.ui.activity.FolderPickerActivity".plus(".EXTRA_FOLDER") @JvmField @Deprecated(