From 26127ae3619fcf1c63b36c3e9752fa6f09bd75ba Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Jan 2026 15:06:44 +0100 Subject: [PATCH] fix(unified-search): screen state Signed-off-by: alperozturk96 --- .../ui/fragment/UnifiedSearchFragment.kt | 61 ++++++++++--------- .../UnifiedSearchFragmentScreenState.kt | 38 ++++++++++++ .../unifiedsearch/IUnifiedSearchViewModel.kt | 4 ++ .../unifiedsearch/UnifiedSearchViewModel.kt | 7 +++ 4 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 74f9248640e3..46ef6486c751 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -38,6 +38,7 @@ import com.nextcloud.client.network.ClientFactory import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.extensions.getTypedActivity import com.nextcloud.utils.extensions.searchFilesByName +import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.extensions.typedActivity import com.owncloud.android.R import com.owncloud.android.databinding.ListFragmentBinding @@ -230,47 +231,41 @@ class UnifiedSearchFragment : adapter.setData(emptyList()) adapter.setDataCurrentDirItems(listOf()) - showStartYourSearch() + vm.updateScreenState(UnifiedSearchFragmentScreenState.Empty.startSearch()) showKeyboard(searchView) } } - private fun makeEmptyListVisible() { - binding.emptyList.run { - root.visibility = View.VISIBLE - emptyListIcon.visibility = View.VISIBLE - emptyListViewHeadline.visibility = View.VISIBLE - emptyListViewText.visibility = View.VISIBLE - emptyListIcon.visibility = View.VISIBLE + private fun handleScreenState(state: UnifiedSearchFragmentScreenState) { + when (state) { + is UnifiedSearchFragmentScreenState.ShowingContent -> { + toggleEmptyListVisible(show = false) + } + is UnifiedSearchFragmentScreenState.Empty -> { + showEmptyView(state) + } } } - private fun showStartYourSearch() { - makeEmptyListVisible() - + private fun toggleEmptyListVisible(show: Boolean) { binding.emptyList.run { - emptyListViewHeadline.text = getString(R.string.file_list_empty_unified_search_start_search) - emptyListViewText.text = getString(R.string.file_list_empty_unified_search_start_search_description) - emptyListIcon.setImageDrawable( - viewThemeUtils.platform.tintDrawable( - requireContext(), - R.drawable.ic_search_grey - ) - ) + root.setVisibleIf(show) + emptyListIcon.setVisibleIf(show) + emptyListViewHeadline.setVisibleIf(show) + emptyListViewText.setVisibleIf(show) + emptyListIcon.setVisibleIf(show) } } - private fun showNoResult() { - makeEmptyListVisible() + private fun showEmptyView(state: UnifiedSearchFragmentScreenState.Empty) { + toggleEmptyListVisible(show = true) binding.emptyList.run { - emptyListViewHeadline.text = - requireContext().getString(R.string.file_list_empty_headline_server_search) - emptyListViewText.text = - requireContext().getString(R.string.file_list_empty_unified_search_no_results) emptyListIcon.setImageDrawable( - viewThemeUtils.platform.tintDrawable(requireContext(), R.drawable.ic_search_grey) + viewThemeUtils.platform.tintDrawable(requireContext(), state.iconId) ) + emptyListViewHeadline.text = requireContext().getString(state.titleId) + emptyListViewText.text = requireContext().getString(state.descriptionId) } } @@ -317,6 +312,9 @@ class UnifiedSearchFragment : vm.isLoading.observe(viewLifecycleOwner) { loading -> binding.swipeContainingList.isRefreshing = loading } + vm.screenState.observe(viewLifecycleOwner) { + handleScreenState(it) + } PairMediatorLiveData(vm.searchResults, vm.isLoading).observe(viewLifecycleOwner) { (searchResults, isLoading) -> if (isLoading == true || searchResults.isNullOrEmpty()) { @@ -329,7 +327,7 @@ class UnifiedSearchFragment : !hasSearchResult && !adapter.hasLocalResults() ) { - showNoResult() + vm.updateScreenState(UnifiedSearchFragmentScreenState.Empty.noResults()) } } @@ -419,7 +417,11 @@ class UnifiedSearchFragment : fun onSearchResultChanged(result: List) { Log_OC.d(TAG, "result") binding.emptyList.emptyListView.visibility = View.GONE - adapter.setData(result.filterOutHiddenFiles(listOfHiddenFiles)) + val newFiles = result.filterOutHiddenFiles(listOfHiddenFiles) + if (newFiles.isNotEmpty()) { + vm.updateScreenState(UnifiedSearchFragmentScreenState.ShowingContent) + } + adapter.setData(newFiles) } @VisibleForTesting @@ -446,6 +448,9 @@ class UnifiedSearchFragment : val files = storageManager .searchFilesByName(this, accountManager.user.accountName, query) .filter { !it.isEncrypted } + if (files.isNotEmpty()) { + vm.updateScreenState(UnifiedSearchFragmentScreenState.ShowingContent) + } adapter.setDataCurrentDirItems(files) } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt new file mode 100644 index 000000000000..8a586b5f6583 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragmentScreenState.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.fragment + +import com.owncloud.android.R + +sealed class UnifiedSearchFragmentScreenState { + + /** + * Content is being displayed (search results or current directory items) + */ + object ShowingContent : UnifiedSearchFragmentScreenState() + + /** + * Empty state with customizable message + */ + data class Empty(val titleId: Int, val descriptionId: Int, val iconId: Int) : UnifiedSearchFragmentScreenState() { + + companion object { + fun startSearch() = Empty( + titleId = R.string.file_list_empty_unified_search_start_search, + descriptionId = R.string.file_list_empty_unified_search_start_search_description, + iconId = R.drawable.ic_search_grey + ) + + fun noResults() = Empty( + titleId = R.string.file_list_empty_headline_server_search, + descriptionId = R.string.file_list_empty_unified_search_no_results, + iconId = R.drawable.ic_search_grey + ) + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt index 04c7d0e00d52..6cc2c6653b35 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchViewModel.kt @@ -8,10 +8,13 @@ package com.owncloud.android.ui.unifiedsearch import android.net.Uri import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.fragment.UnifiedSearchFragmentScreenState interface IUnifiedSearchViewModel { + val screenState: MutableLiveData val browserUri: LiveData val error: LiveData val file: LiveData @@ -25,4 +28,5 @@ interface IUnifiedSearchViewModel { fun setQuery(query: String) fun openFile(remotePath: String) fun getRemoteFile(remotePath: String) + fun updateScreenState(state: UnifiedSearchFragmentScreenState) } diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt index 4afb9ed10948..a68756f86a72 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt @@ -25,6 +25,7 @@ import com.owncloud.android.lib.common.SearchResult import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.asynctasks.GetRemoteFileTask +import com.owncloud.android.ui.fragment.UnifiedSearchFragmentScreenState import javax.inject.Inject @Suppress("LongParameterList") @@ -53,6 +54,8 @@ class UnifiedSearchViewModel(application: Application) : private lateinit var repository: IUnifiedSearchRepository private var results: MutableMap = mutableMapOf() + override val screenState: MutableLiveData = + MutableLiveData(UnifiedSearchFragmentScreenState.ShowingContent) override val isLoading = MutableLiveData(false) override val searchResults = MutableLiveData>(mutableListOf()) override val error = MutableLiveData("") @@ -244,4 +247,8 @@ class UnifiedSearchViewModel(application: Application) : fun setConnectivityService(connectivityService: ConnectivityService) { this.connectivityService = connectivityService } + + override fun updateScreenState(state: UnifiedSearchFragmentScreenState) { + screenState.value = state + } }