소스 검색

Unified search: always show files results first

Signed-off-by: Álvaro Brey Vilas <alvaro.brey@nextcloud.com>
Álvaro Brey Vilas 3 년 전
부모
커밋
02d133a1c7

+ 0 - 151
src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.java

@@ -1,151 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Tobias Kaminsky
- * @author Chris Narkiewicz <hello@ezaquarii.com>
- *
- * Copyright (C) 2018 Tobias Kaminsky
- * Copyright (C) 2018 Nextcloud
- * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.adapter;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
-import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
-import com.elyeproj.loaderviewlibrary.LoaderImageView;
-import com.nextcloud.client.account.User;
-import com.nextcloud.client.network.ClientFactory;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.UnifiedSearchHeaderBinding;
-import com.owncloud.android.databinding.UnifiedSearchItemBinding;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.lib.common.SearchResult;
-import com.owncloud.android.lib.common.SearchResultEntry;
-import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import kotlin.NotImplementedError;
-
-/**
- * This Adapter populates a SectionedRecyclerView with search results by unified search
- */
-public class UnifiedSearchListAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> {
-
-    private List<SearchResult> list = new ArrayList<>();
-
-    private FileDataStorageManager storageManager;
-
-    private Context context;
-    private UnifiedSearchListInterface listInterface;
-    private User user;
-    private ClientFactory clientFactory;
-
-    public UnifiedSearchListAdapter(FileDataStorageManager storageManager,
-                                    UnifiedSearchListInterface listInterface,
-                                    User user,
-                                    ClientFactory clientFactory,
-                                    Context context) {
-        this.storageManager = storageManager;
-        this.listInterface = listInterface;
-        this.user = user;
-        this.clientFactory = clientFactory;
-        this.context = context;
-
-        // initialise thumbnails cache on background thread
-        new ThumbnailsCacheManager.InitDiskCacheTask().execute();
-    }
-
-    @NonNull
-    @Override
-    public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        if (viewType == VIEW_TYPE_HEADER) {
-            UnifiedSearchHeaderBinding binding = UnifiedSearchHeaderBinding.inflate(LayoutInflater.from(context),
-                                                                                    parent,
-                                                                                    false);
-
-            return new UnifiedSearchHeaderViewHolder(binding, context);
-        } else {
-            UnifiedSearchItemBinding binding = UnifiedSearchItemBinding.inflate(LayoutInflater.from(context),
-                                                                                parent,
-                                                                                false);
-
-            return new UnifiedSearchItemViewHolder(binding,
-                                                   user,
-                                                   clientFactory,
-                                                   storageManager,
-                                                   listInterface,
-                                                   context);
-        }
-    }
-
-    @Override
-    public int getSectionCount() {
-        return list.size();
-    }
-
-    @Override
-    public int getItemCount(int section) {
-        return list.get(section).getEntries().size();
-    }
-
-    @Override
-    public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, boolean expanded) {
-        UnifiedSearchHeaderViewHolder headerViewHolder = (UnifiedSearchHeaderViewHolder) holder;
-
-        headerViewHolder.bind(list.get(section));
-    }
-
-    @Override
-    public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
-        throw new NotImplementedError();
-    }
-
-    @Override
-    public void onBindViewHolder(SectionedViewHolder holder, int section, int relativePosition, int absolutePosition) {
-        // TODO different binding (and also maybe diff UI) for non-file results
-        UnifiedSearchItemViewHolder itemViewHolder = (UnifiedSearchItemViewHolder) holder;
-        SearchResultEntry entry = list.get(section).getEntries().get(relativePosition);
-
-        itemViewHolder.bind(entry);
-    }
-
-    @Override
-    public void onViewAttachedToWindow(@NonNull SectionedViewHolder holder) {
-        if (holder instanceof UnifiedSearchItemViewHolder) {
-            LoaderImageView thumbnailShimmer = ((UnifiedSearchItemViewHolder) holder).getBinding().thumbnailShimmer;
-            if (thumbnailShimmer.getVisibility() == View.VISIBLE) {
-                thumbnailShimmer.setImageResource(R.drawable.background);
-                thumbnailShimmer.resetLoader();
-            }
-        }
-    }
-
-    public void setList(List<SearchResult> list) {
-        this.list = list;
-        notifyDataSetChanged();
-    }
-
-}

+ 148 - 0
src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt

@@ -0,0 +1,148 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * @author Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * Copyright (C) 2018 Tobias Kaminsky
+ * Copyright (C) 2018 Nextcloud
+ * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter
+
+import android.content.Context
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface
+import com.nextcloud.client.account.User
+import com.nextcloud.client.network.ClientFactory
+import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
+import com.afollestad.sectionedrecyclerview.SectionedViewHolder
+import com.owncloud.android.lib.common.SearchResult
+import java.util.ArrayList
+import android.view.ViewGroup
+import android.view.LayoutInflater
+import android.view.View
+import kotlin.NotImplementedError
+import com.owncloud.android.R
+import com.owncloud.android.databinding.UnifiedSearchHeaderBinding
+import com.owncloud.android.databinding.UnifiedSearchItemBinding
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.InitDiskCacheTask
+
+/**
+ * This Adapter populates a SectionedRecyclerView with search results by unified search
+ */
+class UnifiedSearchListAdapter(
+    private val storageManager: FileDataStorageManager,
+    private val listInterface: UnifiedSearchListInterface,
+    private val user: User,
+    private val clientFactory: ClientFactory,
+    private val context: Context
+) : SectionedRecyclerViewAdapter<SectionedViewHolder>() {
+    companion object {
+        private const val FILES_PROVIDER_ID = "files"
+    }
+
+    private var list: List<SearchResult> = ArrayList()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionedViewHolder {
+        return if (viewType == VIEW_TYPE_HEADER) {
+            val binding = UnifiedSearchHeaderBinding.inflate(
+                LayoutInflater.from(
+                    context
+                ),
+                parent,
+                false
+            )
+            UnifiedSearchHeaderViewHolder(binding, context)
+        } else {
+            val binding = UnifiedSearchItemBinding.inflate(
+                LayoutInflater.from(
+                    context
+                ),
+                parent,
+                false
+            )
+            UnifiedSearchItemViewHolder(
+                binding,
+                user,
+                clientFactory,
+                storageManager,
+                listInterface,
+                context
+            )
+        }
+    }
+
+    override fun getSectionCount(): Int {
+        return list.size
+    }
+
+    override fun getItemCount(section: Int): Int {
+        return list[section].entries.size
+    }
+
+    override fun onBindHeaderViewHolder(holder: SectionedViewHolder, section: Int, expanded: Boolean) {
+        val headerViewHolder = holder as UnifiedSearchHeaderViewHolder
+        headerViewHolder.bind(list[section])
+    }
+
+    override fun onBindFooterViewHolder(holder: SectionedViewHolder, section: Int) {
+        throw NotImplementedError()
+    }
+
+    override fun onBindViewHolder(
+        holder: SectionedViewHolder,
+        section: Int,
+        relativePosition: Int,
+        absolutePosition: Int
+    ) {
+        // TODO different binding (and also maybe diff UI) for non-file results
+        val itemViewHolder = holder as UnifiedSearchItemViewHolder
+        val entry = list[section].entries[relativePosition]
+        itemViewHolder.bind(entry)
+    }
+
+    override fun onViewAttachedToWindow(holder: SectionedViewHolder) {
+        if (holder is UnifiedSearchItemViewHolder) {
+            val thumbnailShimmer = holder.binding.thumbnailShimmer
+            if (thumbnailShimmer.visibility == View.VISIBLE) {
+                thumbnailShimmer.setImageResource(R.drawable.background)
+                thumbnailShimmer.resetLoader()
+            }
+        }
+    }
+
+    fun setData(results: Map<String, SearchResult>) {
+        // "Files" always goes first
+        val comparator =
+            Comparator { o1: Map.Entry<String, SearchResult>, o2: Map.Entry<String, SearchResult> ->
+                when {
+                    o1.key == FILES_PROVIDER_ID -> -1
+                    o2.key == FILES_PROVIDER_ID -> 1
+                    else -> 0
+                }
+            }
+
+        list = results.asSequence().sortedWith(comparator).map { it.value }.toList()
+        // TODO only update where needed
+        notifyDataSetChanged()
+    }
+
+    init {
+        // initialise thumbnails cache on background thread
+        InitDiskCacheTask().execute()
+    }
+}

+ 2 - 2
src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt

@@ -145,10 +145,10 @@ class UnifiedSearchFragment : Fragment(), Injectable, UnifiedSearchListInterface
     }
 
     @VisibleForTesting
-    fun onSearchResultChanged(result: MutableMap<String, SearchResult>) {
+    fun onSearchResultChanged(result: Map<String, SearchResult>) {
         binding.emptyList.emptyListView.visibility = View.GONE
 
-        adapter.setList(result.values.toList())
+        adapter.setData(result)
     }
 
     @VisibleForTesting

+ 5 - 2
src/main/java/com/owncloud/android/ui/unifiedsearch/SearchOnProviderTask.kt

@@ -29,14 +29,17 @@ class SearchOnProviderTask(
     private val provider: String,
     private val client: NextcloudClient
 ) : () -> SearchOnProviderTask.Result {
+    companion object {
+        private const val TAG = "SearchOnProviderTask"
+    }
 
     data class Result(val success: Boolean = false, val searchResult: SearchResult = SearchResult())
 
     override fun invoke(): Result {
-        Log_OC.d("Unified Search", "Run task")
+        Log_OC.d(TAG, "Run task")
         val result = UnifiedSearchRemoteOperation(provider, query).execute(client)
 
-        Log_OC.d("Unified Search", "Task finished: " + result.isSuccess)
+        Log_OC.d(TAG, "Task finished: " + result.isSuccess)
         return if (result.isSuccess && result.resultData != null) {
             Result(
                 success = true,

+ 0 - 63
src/main/java/com/owncloud/android/ui/unifiedsearch/SearchOnProvidersTask.kt

@@ -1,63 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Chris Narkiewicz
- * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.unifiedsearch
-
-import com.nextcloud.android.lib.resources.search.UnifiedSearchRemoteOperation
-import com.nextcloud.common.NextcloudClient
-import com.owncloud.android.lib.common.SearchResult
-import com.owncloud.android.lib.common.operations.RemoteOperationResult
-import com.owncloud.android.lib.common.utils.Log_OC
-
-class SearchOnProvidersTask(
-    private val query: String,
-    private val providers: List<String>,
-    private val client: NextcloudClient
-) : () -> SearchOnProvidersTask.Result {
-
-    companion object {
-        const val TAG = "SearchOnProvidersTask"
-    }
-
-    data class Result(val success: Boolean = false, val searchResults: Map<ProviderID, SearchResult> = emptyMap())
-
-    override fun invoke(): Result {
-
-        Log_OC.d(TAG, "Run task")
-        val results: List<Pair<String, RemoteOperationResult<SearchResult>>> = providers
-            .map { UnifiedSearchRemoteOperation(it, query) }
-            .map { Pair(it.provider, it.execute(client)) }
-
-        val success = results.isNotEmpty() && results.any { it.second.isSuccess }
-        Log_OC.d(TAG, "Task finished, success: $success")
-        Log_OC.d(TAG, "Providers successful: ${results.count { it.second.isSuccess }}")
-        Log_OC.d(TAG, "Providers successful: ${results.count { !it.second.isSuccess }}")
-
-        return when {
-            success -> Result(
-                success = true,
-                searchResults = results
-                    .filter { it.second.isSuccess }
-                    .map { Pair(it.first, it.second.resultData) }
-                    .toMap()
-            )
-            else -> Result()
-        }
-    }
-}

+ 1 - 0
src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt

@@ -144,6 +144,7 @@ class UnifiedSearchViewModel() : ViewModel() {
     }
 
     fun onSearchFinished(success: Boolean) {
+        Log_OC.d(TAG, "onSearchFinished: success: $success")
         isLoading.value = false
         if (!success) {
             error.value = resources.getString(R.string.search_error)