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 79925570bbac..df5c184aa0ca 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 @@ -278,6 +278,7 @@ class FileDisplayActivity : observeWorkerState() startMetadataSyncForRoot() handleBackPress() + registerUnifiedSearchReceiver() } private fun loadSavedInstanceState(savedInstanceState: Bundle?) { @@ -731,7 +732,13 @@ class FileDisplayActivity : val transaction = fragmentManager.beginTransaction() transaction.addToBackStack(null) transaction.replace(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES) - transaction.commit() + + if (fragmentManager.isStateSaved) { + transaction.commitAllowingStateLoss() + } else { + transaction.commit() + } + callback.onComplete(true) } else { callback.onComplete(false) @@ -1280,7 +1287,9 @@ class FileDisplayActivity : searchView?.setQuery("", false) searchView?.onActionViewCollapsed() - if (isRoot(getCurrentDir()) && leftFragment is OCFileListFragment) { + val isRoot = isRoot(getCurrentDir()) + + if (isRoot && leftFragment is OCFileListFragment) { // Remove the list to the original state leftFragment.adapter?.let { adapter -> val listOfHiddenFiles = adapter.listOfHiddenFiles @@ -1293,6 +1302,13 @@ class FileDisplayActivity : if (leftFragment is UnifiedSearchFragment) { showSortListGroup(false) supportFragmentManager.popBackStack() + + // needed to set correct action bar style + if (isRoot) { + setupHomeSearchToolbarWithSortAndListButtons() + } else { + setupToolbar() + } } } @@ -1953,7 +1969,7 @@ class FileDisplayActivity : else -> VirtualFolderType.NONE } - startImagePreview(file, type, file.isDown) + startImagePreview(file, file.isDown, type) } else { startImagePreview(file, file.isDown) } @@ -2482,44 +2498,33 @@ class FileDisplayActivity : } } - fun startImagePreview(file: OCFile, showPreview: Boolean) { - val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) - showDetailsIntent.putExtra(EXTRA_FILE, file) - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) - showDetailsIntent.putExtra( - EXTRA_USER, - user.orElseThrow(Supplier { RuntimeException() }) - ) - if (showPreview) { - startActivity(showDetailsIntent) - } else { - val fileOperationsHelper = - FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + fun startImagePreview(file: OCFile, preview: Boolean, type: VirtualFolderType? = null) { + val optionalUser = user + if (optionalUser.isEmpty) { + Log_OC.e(TAG, "user is empty cannot preview image") + return } - } - fun startImagePreview(file: OCFile, type: VirtualFolderType?, showPreview: Boolean) { - val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) - showDetailsIntent.putExtra(EXTRA_FILE, file) - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) - showDetailsIntent.putExtra( - EXTRA_USER, - user.orElseThrow(Supplier { RuntimeException() }) - ) - showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type) + val intent = Intent(this, PreviewImageActivity::class.java).apply { + putExtra(EXTRA_FILE, file) + putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) + putExtra(EXTRA_USER, optionalUser.get()) + putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type) + putExtra(PreviewImageActivity.EXTRA_LAST_SEARCH_QUERY, listOfFilesFragment?.lastSearchQuery) + } - if (showPreview) { - startActivity(showDetailsIntent) - } else { - val fileOperationsHelper = FileOperationsHelper( - this, - userAccountManager, - connectivityService, - editorUtils - ) - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + if (preview) { + startActivity(intent) + return } + + val operation = FileOperationsHelper( + this, + userAccountManager, + connectivityService, + editorUtils + ) + operation.startSyncForFileAndIntent(file, intent) } /** @@ -2782,8 +2787,8 @@ class FileDisplayActivity : val virtualType = bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE) as VirtualFolderType? startImagePreview( file, - virtualType, - true + true, + virtualType ) } else { startImagePreview(file, true) @@ -3043,23 +3048,39 @@ class FileDisplayActivity : binding.fabMain.visibility = visibility } - fun showFile(selectedFile: OCFile?, message: String?) { - dismissLoadingDialog() + private fun registerUnifiedSearchReceiver() { + val filter = IntentFilter(UNIFIED_SEARCH_EVENT_ACTION) + LocalBroadcastManager.getInstance(this).registerReceiver(unifiedSearchReceiver, filter) + } + + private val unifiedSearchReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Log_OC.d(TAG, "unified search receiver called") + val query = intent.getStringExtra(PreviewImageActivity.EXTRA_LAST_SEARCH_QUERY) ?: return + listOfFilesFragment?.lastSearchQuery = null + performUnifiedSearch(query, null) + } + } + @JvmOverloads + fun showFile(selectedFile: OCFile?, message: String?, lastSearchQuery: String? = null) { getOCFileListFragmentFromFile(object : TransactionInterface { override fun onOCFileListFragmentComplete(listOfFiles: OCFileListFragment) { - if (TextUtils.isEmpty(message)) { + dismissLoadingDialog() + + if (message?.isEmpty() == true) { val temp = file file = getCurrentDir() listOfFiles.listDirectory(getCurrentDir(), temp, MainApp.isOnlyOnDevice()) updateActionBarTitleAndHomeButton(null) } else { - val view = listOfFiles.view - if (view != null) { - DisplayUtils.showSnackMessage(view, message) + listOfFiles.view?.let { + DisplayUtils.showSnackMessage(it, message) } } + if (selectedFile != null) { + listOfFiles.lastSearchQuery = lastSearchQuery listOfFiles.onItemClicked(selectedFile) } } @@ -3124,6 +3145,8 @@ class FileDisplayActivity : const val ACTION_DETAILS: String = "com.owncloud.android.ui.activity.action.DETAILS" + const val UNIFIED_SEARCH_EVENT_ACTION = "PHOTO_SEARCH_EVENT" + @JvmField val REQUEST_CODE__SELECT_CONTENT_FROM_APPS: Int = REQUEST_CODE__LAST_SHARED + 1 diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index fcc8f4661f39..75c25168e9ad 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -9,6 +9,7 @@ package com.owncloud.android.ui.adapter import android.content.Context import android.view.View +import androidx.lifecycle.lifecycleScope import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.common.NextcloudClient @@ -17,21 +18,27 @@ import com.nextcloud.utils.CalendarEventManager import com.nextcloud.utils.ContactManager import com.nextcloud.utils.GlideHelper import com.nextcloud.utils.extensions.getType +import com.nextcloud.utils.extensions.getTypedActivity import com.owncloud.android.databinding.UnifiedSearchItemBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.fragment.UnifiedSearchFragment import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @Suppress("LongParameterList") class UnifiedSearchItemViewHolder( + private val fragment: UnifiedSearchFragment, private val supportsOpeningCalendarContactsLocally: Boolean, val binding: UnifiedSearchItemBinding, private val storageManager: FileDataStorageManager, private val listInterface: UnifiedSearchListInterface, private val filesAction: FilesAction, val context: Context, - private val nextcloudClient: NextcloudClient, private val viewThemeUtils: ViewThemeUtils ) : SectionedViewHolder(binding.root) { @@ -41,6 +48,7 @@ class UnifiedSearchItemViewHolder( private val contactManager = ContactManager(context) private val calendarEventManager = CalendarEventManager(context) + private var cachedClient: NextcloudClient? = null fun bind(entry: SearchResultEntry) { binding.title.text = entry.title @@ -54,14 +62,24 @@ class UnifiedSearchItemViewHolder( val entryType = entry.getType() viewThemeUtils.platform.colorImageView(binding.thumbnail, ColorRole.PRIMARY) - GlideHelper.loadIntoImageView( - context, - nextcloudClient, - entry.thumbnailUrl, - binding.thumbnail, - entryType.iconId(), - circleCrop = entry.rounded - ) + + val client = cachedClient + if (client != null) { + loadThumbnailUrl(client, entry, entryType) + } else { + fragment.lifecycleScope.launch(Dispatchers.IO) { + val newClient = fragment.getTypedActivity(FileActivity::class.java) + ?.clientRepository + ?.getNextcloudClient() + ?: return@launch + + cachedClient = newClient + + withContext(Dispatchers.Main) { + loadThumbnailUrl(newClient, entry, entryType) + } + } + } if (entry.isFile) { binding.more.visibility = View.VISIBLE @@ -77,6 +95,17 @@ class UnifiedSearchItemViewHolder( } } + private fun loadThumbnailUrl(client: NextcloudClient, entry: SearchResultEntry, entryType: SearchResultEntryType) { + GlideHelper.loadIntoImageView( + context, + client, + entry.thumbnailUrl, + binding.thumbnail, + entryType.iconId(), + circleCrop = entry.rounded + ) + } + private fun searchEntryOnClick(entry: SearchResultEntry, entryType: SearchResultEntryType) { if (supportsOpeningCalendarContactsLocally) { when (entryType) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt index 6e5b0c6a4f20..46dedad2d5a3 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt @@ -17,7 +17,6 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.nextcloud.client.account.User import com.nextcloud.client.preferences.AppPreferences -import com.nextcloud.common.NextcloudClient import com.owncloud.android.R import com.owncloud.android.databinding.UnifiedSearchCurrentDirectoryItemBinding import com.owncloud.android.databinding.UnifiedSearchEmptyBinding @@ -28,6 +27,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.ThumbnailsCacheManager +import com.owncloud.android.ui.fragment.UnifiedSearchFragment import com.owncloud.android.ui.interfaces.UnifiedSearchCurrentDirItemAction import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection @@ -39,6 +39,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils */ @Suppress("LongParameterList") class UnifiedSearchListAdapter( + private val fragment: UnifiedSearchFragment, private val supportsOpeningCalendarContactsLocally: Boolean, private val storageManager: FileDataStorageManager, private val listInterface: UnifiedSearchListInterface, @@ -48,7 +49,6 @@ class UnifiedSearchListAdapter( private val viewThemeUtils: ViewThemeUtils, private val appPreferences: AppPreferences, private val syncedFolderProvider: SyncedFolderProvider, - private val nextcloudClient: NextcloudClient, private val currentDirItemAction: UnifiedSearchCurrentDirItemAction ) : SectionedRecyclerViewAdapter() { companion object { @@ -85,13 +85,13 @@ class UnifiedSearchListAdapter( false ) UnifiedSearchItemViewHolder( + fragment, supportsOpeningCalendarContactsLocally, binding, storageManager, listInterface, filesAction, context, - nextcloudClient, viewThemeUtils ) } 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 cfe5f17f3f57..41acedf12331 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 @@ -225,6 +225,7 @@ public class OCFileListFragment extends ExtendedListFragment implements protected String mLimitToMimeType; private FloatingActionButton mFabMain; public static boolean isMultipleFileSelectedForCopyOrMove = false; + private String lastSearchQuery = null; @Inject DeviceInfo deviceInfo; @@ -253,6 +254,15 @@ public void onCreate(Bundle savedInstanceState) { searchFragment = currentSearchType != null && isSearchEventSet(searchEvent); } + public void setLastSearchQuery(@Nullable String value) { + lastSearchQuery = value; + } + + @Nullable + public String getLastSearchQuery() { + return lastSearchQuery; + } + @Override public void onResume() { // Don't handle search events if we're coming back from back stack 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..3e609dd361b8 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 @@ -26,7 +26,6 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.account.UserAccountManager @@ -36,7 +35,6 @@ import com.nextcloud.client.di.Injectable import com.nextcloud.client.di.ViewModelFactory 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.typedActivity import com.owncloud.android.R @@ -47,7 +45,6 @@ import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.status.NextcloudVersion -import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.adapter.UnifiedSearchItemViewHolder import com.owncloud.android.ui.adapter.UnifiedSearchListAdapter @@ -62,9 +59,6 @@ import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.PermissionUtil import com.owncloud.android.utils.theme.ViewThemeUtils -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject /** @@ -209,7 +203,7 @@ class UnifiedSearchFragment : // Because this fragment is opened with TextView onClick on the previous screen maxWidth = Integer.MAX_VALUE viewThemeUtils.androidx.themeToolbarSearchView(this) - setQuery(vm.query.value ?: initialQuery, false) + setQuery(getLastSearchQuery(), false) setOnQueryTextListener(this@UnifiedSearchFragment) isIconified = false clearFocus() @@ -362,47 +356,42 @@ class UnifiedSearchFragment : if (showFileActions) { fda.showFileActions(file) } else { - fda.showFile(file, "") + fda.showFile(file, "", getLastSearchQuery()) } } } } + private fun getLastSearchQuery(): String? = vm.query.value ?: initialQuery + private fun setupAdapter() { val syncedFolderProvider = SyncedFolderProvider(requireContext().contentResolver, appPreferences, clock) val gridLayoutManager = GridLayoutManager(requireContext(), 1) - lifecycleScope.launch(Dispatchers.IO) { - val client = - getTypedActivity(FileActivity::class.java)?.clientRepository?.getNextcloudClient() ?: return@launch - - withContext(Dispatchers.Main) { - adapter = UnifiedSearchListAdapter( - supportsOpeningCalendarContactsLocally(), - storageManager, - this@UnifiedSearchFragment, - this@UnifiedSearchFragment, - currentAccountProvider.user, - requireContext(), - viewThemeUtils, - appPreferences, - syncedFolderProvider, - client, - this@UnifiedSearchFragment - ) - - adapter.shouldShowFooters(true) - adapter.setLayoutManager(gridLayoutManager) - binding.listRoot.layoutManager = gridLayoutManager - binding.listRoot.adapter = adapter - searchInCurrentDirectory(initialQuery ?: "") + adapter = UnifiedSearchListAdapter( + this@UnifiedSearchFragment, + supportsOpeningCalendarContactsLocally(), + storageManager, + this@UnifiedSearchFragment, + this@UnifiedSearchFragment, + currentAccountProvider.user, + requireContext(), + viewThemeUtils, + appPreferences, + syncedFolderProvider, + this@UnifiedSearchFragment + ) + + adapter.shouldShowFooters(true) + adapter.setLayoutManager(gridLayoutManager) + binding.listRoot.layoutManager = gridLayoutManager + binding.listRoot.adapter = adapter + searchInCurrentDirectory(initialQuery ?: "") - setUpViewModel() - if (!initialQuery.isNullOrEmpty()) { - vm.setQuery(initialQuery!!) - vm.initialQuery() - } - } + setUpViewModel() + if (!initialQuery.isNullOrEmpty()) { + vm.setQuery(initialQuery!!) + vm.initialQuery() } } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 3ebde7fb9324..96d4b6a8f67c 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -237,8 +237,6 @@ class PreviewImageActivity : return super.onOptionsItemSelected(item) } - sendRefreshSearchEventBroadcast() - if (isDrawerOpen) { closeDrawer() } else { @@ -249,8 +247,17 @@ class PreviewImageActivity : } private fun sendRefreshSearchEventBroadcast() { - val intent = Intent(GalleryFragment.REFRESH_SEARCH_EVENT_RECEIVER) - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + val broadcastManager = LocalBroadcastManager.getInstance(this) + + val photoSearchIntent = Intent(GalleryFragment.REFRESH_SEARCH_EVENT_RECEIVER) + broadcastManager.sendBroadcast(photoSearchIntent) + + intent.getStringExtra(EXTRA_LAST_SEARCH_QUERY)?.let { + val unifiedSearchIntent = Intent(FileDisplayActivity.UNIFIED_SEARCH_EVENT_ACTION).apply { + putExtra(EXTRA_LAST_SEARCH_QUERY, it) + } + broadcastManager.sendBroadcast(unifiedSearchIntent) + } } public override fun onStart() { @@ -591,6 +598,8 @@ class PreviewImageActivity : companion object { val TAG: String = PreviewImageActivity::class.java.simpleName const val EXTRA_VIRTUAL_TYPE: String = "EXTRA_VIRTUAL_TYPE" + const val EXTRA_LAST_SEARCH_QUERY: String = "EXTRA_LAST_SEARCH_QUERY" + private const val KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER" private const val KEY_SYSTEM_VISIBLE = "TRUE"