From 232fa2ea6e61d7ac133dd8735505620eba43e63e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Oct 2026 12:30:07 +0300 Subject: [PATCH 1/3] unify fab state logic Signed-off-by: alperozturk96 # Conflicts: # app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt --- .../client/FileDisplayActivityScreenshotIT.kt | 9 +-- .../ui/fragment/ExtendedListFragment.kt | 2 +- .../owncloud/android/ui/fragment/FabState.kt | 13 ++++ .../android/ui/fragment/GalleryFragment.kt | 2 +- .../android/ui/fragment/OCFileListFragment.kt | 67 ++++++++----------- .../OCFileListMultiChoiceModeListener.kt | 6 +- 6 files changed, 50 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt index ca13d25070e1..8108814ed68b 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityScreenshotIT.kt @@ -23,6 +23,7 @@ import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.fragment.EmptyListState +import com.owncloud.android.ui.fragment.FabState import com.owncloud.android.utils.ScreenshotTest import org.junit.Assert import org.junit.Rule @@ -48,7 +49,7 @@ class FileDisplayActivityScreenshotIT : AbstractIT() { scenario.onActivity { sut -> sut.run { listOfFilesFragment?.let { - it.setFabEnabled(false) + it.setFabState(FabState.DISABLED) resetScrolling(true) it.setEmptyListMessage(EmptyListState.LOADING) it.isLoading = false @@ -78,7 +79,7 @@ class FileDisplayActivityScreenshotIT : AbstractIT() { activity = sut val fragment = sut.listOfFilesFragment Assert.assertNotNull(fragment) - fragment!!.setFabEnabled(false) + fragment!!.setFabState(FabState.DISABLED) fragment.setEmptyListMessage(EmptyListState.LOADING) fragment.isLoading = false } @@ -91,7 +92,7 @@ class FileDisplayActivityScreenshotIT : AbstractIT() { .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) val fragment = activity!!.listOfFilesFragment - fragment!!.setFabEnabled(false) + fragment!!.setFabState(FabState.DISABLED) fragment.setEmptyListMessage(EmptyListState.LOADING) fragment.isLoading = false @@ -117,7 +118,7 @@ class FileDisplayActivityScreenshotIT : AbstractIT() { resetScrolling(true) listOfFilesFragment?.let { - it.setFabEnabled(false) + it.setFabState(FabState.DISABLED) it.setEmptyListMessage(EmptyListState.LOADING) it.isLoading = false } 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 5132ef93c67c..3326e134990f 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 @@ -199,7 +199,7 @@ open class ExtendedListFragment : if (activity is FileDisplayActivity) { val fragment = activity.leftFragment if (fragment is OCFileListFragment) { - fragment.setFabVisible(!hasFocus) + if (hasFocus) fragment.setFabState(FabState.HIDDEN) else fragment.updateFabState() } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt new file mode 100644 index 000000000000..a0d8ef8c50e3 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.fragment + +enum class FabState { + HIDDEN, + DISABLED, + ENABLED +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt index df6cacb57753..a17adade1de7 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt @@ -281,7 +281,7 @@ class GalleryFragment : // always show first stored items showAllGalleryItems() - setFabVisible(false) + setFabState(FabState.HIDDEN) searchAndDisplay() } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt index 01f945d5f4bb..62f3dd9c24fd 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt @@ -379,10 +379,8 @@ open class OCFileListFragment : setAdapter(args) hideFab = args?.getBoolean(ARG_HIDE_FAB, false) == true - if (hideFab) { - setFabVisible(false) - } else { - setFabVisible(currentFile?.let { canCreateInside(it) } ?: true) + updateFabState() + if (!hideFab) { registerFabListener() } @@ -477,38 +475,33 @@ open class OCFileListFragment : } } - fun setFabVisible(visible: Boolean) { - val fab = fabMain ?: return - - val fileDisplayActivity = activity as? FileDisplayActivity - if (fileDisplayActivity != null && - (fileDisplayActivity.isAlbumsFragment || fileDisplayActivity.isAlbumItemsFragment) - ) { - fab.hide() - return - } - - val activity = activity ?: return - activity.runOnUiThread { - if (visible) { - fab.show() - viewThemeUtils.material.themeFAB(fab) - } else { - fab.hide() - } - fab.slideHideBottomBehavior(visible) - } + fun updateFabState() { + setFabState(resolveFabState(currentFile)) } - fun setFabEnabled(enabled: Boolean) { + fun setFabState(state: FabState) { val fab = fabMain ?: return val activity = activity ?: return activity.runOnUiThread { - fab.isEnabled = enabled + val visible = state != FabState.HIDDEN + if (visible) fab.show() else fab.hide() + fab.isEnabled = state == FabState.ENABLED viewThemeUtils.material.themeFAB(fab) + fab.slideHideBottomBehavior(visible) } } + private fun resolveFabState(folder: OCFile?): FabState = when { + hideFab || isSearchFragment || isAlbumScreen() || isReadOnlyFolder(folder) -> FabState.HIDDEN + folder != null && (folder.canCreateFileAndFolder() || folder.isOfflineOperation) -> FabState.ENABLED + else -> FabState.DISABLED + } + + private fun isAlbumScreen(): Boolean { + val fileDisplayActivity = activity as? FileDisplayActivity ?: return false + return fileDisplayActivity.isAlbumsFragment || fileDisplayActivity.isAlbumItemsFragment + } + private fun setBottomNavigationVisible(visible: Boolean) { val drawerActivity = activity as? DrawerActivity ?: return drawerActivity.bottomNavigationView.slideHideBottomBehavior(visible) @@ -519,8 +512,6 @@ open class OCFileListFragment : val storageManager = containerActivity?.storageManager ?: return false return storageManager.isReadOnly(folder) } - - private fun canCreateInside(folder: OCFile): Boolean = folder.canCreateFileAndFolder() && !isReadOnlyFolder(folder) //endregion //region Create and upload actions @@ -801,7 +792,9 @@ open class OCFileListFragment : fileDisplayActivity.previewFile( file, object : CompletionCallback { - override fun onComplete(value: Boolean) = setFabVisible(value) + override fun onComplete(value: Boolean) { + if (value) updateFabState() else setFabState(FabState.HIDDEN) + } } ) @@ -833,7 +826,7 @@ open class OCFileListFragment : when { !file.isEncrypted && fileDisplayActivity != null && fileDisplayActivity.canPreviewInAudioPlayer(file) -> { - setFabVisible(false) + setFabState(FabState.HIDDEN) fileDisplayActivity.startAudioPreview(file, true, true) } @@ -1192,7 +1185,7 @@ open class OCFileListFragment : fun refreshDirectory() { isSearchFragment = false val folder = currentFile ?: return - setFabVisible(canCreateInside(folder)) + updateFabState() listDirectory(folder, MainApp.isOnlyOnDevice()) } @@ -1210,12 +1203,8 @@ open class OCFileListFragment : fileListLayoutManager?.setLayoutViewMode() updateSortButtonLabel() setLayoutSwitchButton() - - val folder = currentFile - val readOnly = isReadOnlyFolder(folder) - setFabVisible(!hideFab && !readOnly) + updateFabState() setBottomNavigationVisible(!hideFab) - setFabEnabled(folder != null && !readOnly && (folder.canCreateFileAndFolder() || folder.isOfflineOperation)) activeActionMode?.invalidate() } @@ -1349,7 +1338,7 @@ open class OCFileListFragment : applySearchEvent(event) isSearchFragment = true - setFabVisible(false) + setFabState(FabState.HIDDEN) mainHandler.post { updateSortButtonLabel() @@ -1446,7 +1435,7 @@ open class OCFileListFragment : updateActionBarTitleAndHomeButton(null) } - currentFile?.let { setFabVisible(canCreateInside(it)) } + updateFabState() setBottomNavigationVisible(true) } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListMultiChoiceModeListener.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListMultiChoiceModeListener.kt index df00d14c251b..fb8947c2d9f2 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListMultiChoiceModeListener.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListMultiChoiceModeListener.kt @@ -93,7 +93,7 @@ internal class OCFileListMultiChoiceModeListener(private val fragment: OCFileLis mode?.invalidate() colorStatusBar(activity) - fragment.setFabVisible(false) + fragment.setFabState(FabState.HIDDEN) if (fragment is GalleryFragment && activity is AlbumsPickerActivity) { placeholderItem.isVisible = false @@ -139,9 +139,7 @@ internal class OCFileListMultiChoiceModeListener(private val fragment: OCFileLis Log_OC.d(TAG, "Selection mode finished") activeActionMode = null - if (!fragment.hideFab && !fragment.isSearchFragment) { - fragment.currentFile?.let { fragment.setFabVisible(it.canCreateFileAndFolder()) } - } + fragment.updateFabState() fragment.activity?.let { fragment.viewThemeUtils.platform.resetStatusBar(it) From cfb75c64e1ac25ffa4f1407b9fc84b8da660c629 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Oct 2026 12:41:27 +0300 Subject: [PATCH 2/3] move logic inside enum Signed-off-by: alperozturk96 --- .../owncloud/android/ui/fragment/FabState.kt | 27 +++++++++++++++- .../android/ui/fragment/OCFileListFragment.kt | 32 ++++--------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt index a0d8ef8c50e3..ae85c629478f 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FabState.kt @@ -6,8 +6,33 @@ */ package com.owncloud.android.ui.fragment +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.nextcloud.utils.extensions.slideHideBottomBehavior +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.utils.theme.ViewThemeUtils + enum class FabState { HIDDEN, DISABLED, - ENABLED + ENABLED; + + fun applyTo(fab: FloatingActionButton, viewThemeUtils: ViewThemeUtils) { + val visible = this != HIDDEN + if (visible) fab.show() else fab.hide() + fab.isEnabled = this == ENABLED + viewThemeUtils.material.themeFAB(fab) + fab.slideHideBottomBehavior(visible) + } + + companion object { + fun from(folder: OCFile?, storageManager: FileDataStorageManager?, hiddenByScreen: Boolean): FabState = when { + hiddenByScreen || folder.isReadOnly(storageManager) -> HIDDEN + folder != null && (folder.canCreateFileAndFolder() || folder.isOfflineOperation) -> ENABLED + else -> DISABLED + } + + private fun OCFile?.isReadOnly(storageManager: FileDataStorageManager?): Boolean = + this != null && storageManager?.isReadOnly(this) == true + } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt index 62f3dd9c24fd..8dc253f99bd3 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt @@ -476,42 +476,22 @@ open class OCFileListFragment : } fun updateFabState() { - setFabState(resolveFabState(currentFile)) + val fileDisplayActivity = activity as? FileDisplayActivity + val isAlbumScreen = fileDisplayActivity?.isAlbumsFragment == true || + fileDisplayActivity?.isAlbumItemsFragment == true + val hiddenByScreen = hideFab || isSearchFragment || isAlbumScreen + setFabState(FabState.from(currentFile, containerActivity?.storageManager, hiddenByScreen)) } fun setFabState(state: FabState) { val fab = fabMain ?: return - val activity = activity ?: return - activity.runOnUiThread { - val visible = state != FabState.HIDDEN - if (visible) fab.show() else fab.hide() - fab.isEnabled = state == FabState.ENABLED - viewThemeUtils.material.themeFAB(fab) - fab.slideHideBottomBehavior(visible) - } - } - - private fun resolveFabState(folder: OCFile?): FabState = when { - hideFab || isSearchFragment || isAlbumScreen() || isReadOnlyFolder(folder) -> FabState.HIDDEN - folder != null && (folder.canCreateFileAndFolder() || folder.isOfflineOperation) -> FabState.ENABLED - else -> FabState.DISABLED - } - - private fun isAlbumScreen(): Boolean { - val fileDisplayActivity = activity as? FileDisplayActivity ?: return false - return fileDisplayActivity.isAlbumsFragment || fileDisplayActivity.isAlbumItemsFragment + activity?.runOnUiThread { state.applyTo(fab, viewThemeUtils) } } private fun setBottomNavigationVisible(visible: Boolean) { val drawerActivity = activity as? DrawerActivity ?: return drawerActivity.bottomNavigationView.slideHideBottomBehavior(visible) } - - private fun isReadOnlyFolder(folder: OCFile?): Boolean { - folder ?: return false - val storageManager = containerActivity?.storageManager ?: return false - return storageManager.isReadOnly(folder) - } //endregion //region Create and upload actions From 3a825808281e7194cc1918e9e805a7f760d9c458 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Oct 2026 13:37:05 +0300 Subject: [PATCH 3/3] wip Signed-off-by: alperozturk96 --- .../com/owncloud/android/ui/activity/FileDisplayActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1e1584ffb0f4..aa382cbe63e6 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 @@ -1097,7 +1097,7 @@ class FileDisplayActivity : file = currentFile currentFile?.let { - listOfFiles.setFabVisible(it.canCreateFileAndFolder()) + listOfFiles.updateFabState() listOfFiles.registerFabListener() }