diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt index 424509890602..1108e7572f65 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt @@ -9,6 +9,10 @@ package com.nextcloud.utils.extensions import com.owncloud.android.MainApp import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.OCFileDepth +import com.owncloud.android.datamodel.OCFileDepth.DeepLevel +import com.owncloud.android.datamodel.OCFileDepth.FirstLevel +import com.owncloud.android.datamodel.OCFileDepth.Root import com.owncloud.android.utils.FileStorageUtils @Suppress("ReturnCount") @@ -55,3 +59,24 @@ fun OCFile?.isPNG(): Boolean { } return "image/png".equals(mimeType, ignoreCase = true) } + +@Suppress("ReturnCount") +fun OCFile?.getDepth(): OCFileDepth? { + if (this == null) { + return null + } + + // Check if it's the root directory + if (this.isRootDirectory) { + return Root + } + + // If parent is root ("/"), this is a direct child of root + val parentPath = this.parentRemotePath ?: return null + if (parentPath == OCFile.ROOT_PATH) { + return FirstLevel + } + + // Otherwise, it's a subdirectory of a subdirectory + return DeepLevel +} diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFileDepth.kt b/app/src/main/java/com/owncloud/android/datamodel/OCFileDepth.kt new file mode 100644 index 000000000000..3e36f901543b --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFileDepth.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel + +enum class OCFileDepth { + Root, + FirstLevel, + DeepLevel +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index bf781c5509c3..3adee261add5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -624,8 +624,7 @@ private void onNavigationItemClicked(final MenuItem menuItem) { UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager()); } } else if (itemId == R.id.nav_shared) { - resetOnlyPersonalAndOnDevice(); - startSharedSearch(menuItem); + openSharedTab(); } else if (itemId == R.id.nav_recently_modified) { resetOnlyPersonalAndOnDevice(); startRecentlyModifiedSearch(menuItem); @@ -686,11 +685,14 @@ public void openAddAccount() { } } - private void startSharedSearch(MenuItem menuItem) { + protected void openSharedTab() { + final var ocFileListFragment = getOCFileListFragment(); + if (ocFileListFragment != null) { + ocFileListFragment.resetFileDepth(); + } + resetOnlyPersonalAndOnDevice(); SearchEvent searchEvent = new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER); - MainApp.showOnlyFilesOnDevice(false); - - launchActivityForSearch(searchEvent, menuItem.getItemId()); + launchActivityForSearch(searchEvent, R.id.nav_shared); } private void startRecentlyModifiedSearch(MenuItem menuItem) { @@ -733,7 +735,7 @@ private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) { * sets the new/current account and restarts. In case the given account equals the actual/current account the call * will be ignored. * - * @param User user to be set + * @param user to be set */ public void accountClicked(User user) { final User currentUser = accountManager.getUser(); 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 d225ba4bff1d..2c5f28432556 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 @@ -92,6 +92,7 @@ import com.owncloud.android.R import com.owncloud.android.databinding.FilesBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.OCFileDepth import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.VirtualFolderType import com.owncloud.android.files.services.NameCollisionPolicy @@ -941,34 +942,10 @@ class FileDisplayActivity : return super.onCreateOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - var retval = true - - val itemId = item.itemId - - if (itemId == android.R.id.home) { - if (!isDrawerOpen && - !isSearchOpen() && - isRoot(getCurrentDir()) && - this.leftFragment is OCFileListFragment - ) { - openDrawer() - } else { - if (isSearchOpen()) { - resetSearchAction() - } else { - onBackPressedDispatcher.onBackPressed() - } - } - } else if (itemId == R.id.action_select_all) { - val fragment = this.listOfFilesFragment - fragment?.selectAllFiles(true) - } else { - retval = super.onOptionsItemSelected(item) - } - - return retval - } + private fun shouldOpenDrawer(): Boolean = !isDrawerOpen && + !isSearchOpen() && + isRoot(getCurrentDir()) && + this.leftFragment is OCFileListFragment /** * Called, when the user selected something for uploading @@ -1193,14 +1170,30 @@ class FileDisplayActivity : leftFragment is OCFileListFragment -> { val fragment = leftFragment as OCFileListFragment - if (isRoot(getCurrentDir())) { - if (fragment.shouldNavigateBackToAllFiles()) { - navigateToAllFiles() - } else { - finish() + + when { + // root + isRoot(getCurrentDir()) -> { + if (fragment.shouldNavigateBackToAllFiles()) { + navigateToAllFiles() + } else { + finish() + } + } + + // shared root + fragment is SharedListFragment && fragment.fileDepth == OCFileDepth.Root -> { + openDrawer() + } + + fragment is SharedListFragment && fragment.fileDepth == OCFileDepth.FirstLevel -> { + openSharedTab() + } + + // Normal folder navigation (go up) also works for shared tab + else -> { + browseUp(fragment) } - } else { - browseUp(fragment) } } @@ -1214,6 +1207,24 @@ class FileDisplayActivity : ) } + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + android.R.id.home -> { + when { + shouldOpenDrawer() -> openDrawer() + isSearchOpen() -> resetSearchAction() + else -> onBackPressedDispatcher.onBackPressed() + } + true + } + + R.id.action_select_all -> { + listOfFilesFragment?.selectAllFiles(true) + true + } + + else -> super.onOptionsItemSelected(item) + } + private fun browseUp(listOfFiles: OCFileListFragment) { listOfFiles.onBrowseUp() val currentFile = listOfFiles.currentFile 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 2525e6de94eb..24fc89811a85 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 @@ -57,6 +57,7 @@ import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.FragmentExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; +import com.nextcloud.utils.extensions.OCFileExtensionsKt; import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.fileNameValidator.FileNameValidator; import com.nextcloud.utils.view.FastScrollUtils; @@ -65,6 +66,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.OCFileDepth; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile; import com.owncloud.android.lib.common.Creator; @@ -241,6 +243,8 @@ protected enum MenuItemAddRemove { private List mOriginalMenuItems = new ArrayList<>(); + private static OCFileDepth fileDepth = OCFileDepth.Root; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -1076,6 +1080,7 @@ public int onBrowseUp() { Future> futureResult = getPreviousFile(); Pair result = futureResult.get(); mFile = result.second; + setFileDepth(mFile); updateFileList(); return result.first; } catch (Exception e) { @@ -1287,7 +1292,20 @@ public void onItemClicked(OCFile file) { } } + private void setFileDepth(OCFile file) { + fileDepth = OCFileExtensionsKt.getDepth(file); + } + + public void resetFileDepth() { + fileDepth = OCFileDepth.Root; + } + + public OCFileDepth getFileDepth() { + return fileDepth; + } + private void browseToFolder(OCFile file, int position) { + setFileDepth(file); resetSearchIfBrowsingFromFavorites(); listDirectory(file, MainApp.isOnlyOnDevice(), false); // then, notify parent activity to let it update its state and view @@ -2309,10 +2327,18 @@ public SearchEvent getSearchEvent() { } public boolean isSearchEventFavorite() { + return isSearchEvent(SearchRemoteOperation.SearchType.FAVORITE_SEARCH); + } + + public boolean isSearchEventShared() { + return isSearchEvent(SearchRemoteOperation.SearchType.SHARED_FILTER); + } + + private boolean isSearchEvent(SearchRemoteOperation.SearchType givenEvent) { if (searchEvent == null) { return false; } - return searchEvent.getSearchType() == SearchRemoteOperation.SearchType.FAVORITE_SEARCH; + return searchEvent.getSearchType() == givenEvent; } public boolean shouldNavigateBackToAllFiles() { diff --git a/app/src/test/java/com/owncloud/android/datamodel/OCFileDepthTests.kt b/app/src/test/java/com/owncloud/android/datamodel/OCFileDepthTests.kt new file mode 100644 index 000000000000..80ea654d1482 --- /dev/null +++ b/app/src/test/java/com/owncloud/android/datamodel/OCFileDepthTests.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel + +import com.nextcloud.utils.extensions.getDepth +import org.junit.Test + +@Suppress("MagicNumber") +class OCFileDepthTests { + + @Test + fun testGetNavStateWhenGivenNullPathShouldReturnNull() { + assert(null.getDepth() == null) + } + + @Test + fun testGetNavStateWhenGivenRootPathShouldReturnRootState() { + val path = "/" + val folder = OCFile(path).apply { + fileId = 1 + remoteId = "01" + decryptedRemotePath = path + } + assert(folder.getDepth() == OCFileDepth.Root) + } + + @Test + fun testGetNavStateWhenGivenRootSubDirOfRootPathShouldReturnSubDirOfRootState() { + val path = "/Abc/" + val folder = OCFile(path).apply { + fileId = 2 + remoteId = "02" + decryptedRemotePath = path + } + assert(folder.getDepth() == OCFileDepth.FirstLevel) + } + + @Test + fun testGetNavStateWhenGivenRootSubDirOfDirPathShouldReturnSubDirOfDirState() { + val path = "/Abc/Ba/" + val folder = OCFile(path).apply { + fileId = 3 + remoteId = "03" + decryptedRemotePath = path + } + assert(folder.getDepth() == OCFileDepth.DeepLevel) + } +}