Browse Source

Unified seach: allow querying different providers asynchronously, and get their IDs in the result

First step towards getting "Load more" working for a single provider

Signed-off-by: Álvaro Brey Vilas <alvaro.brey@nextcloud.com>
Álvaro Brey Vilas 3 years ago
parent
commit
6c98b4cb8d

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

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

+ 9 - 2
src/main/java/com/owncloud/android/ui/unifiedsearch/IUnifiedSearchRepository.kt

@@ -24,10 +24,17 @@ package com.owncloud.android.ui.unifiedsearch
 
 import com.owncloud.android.lib.common.SearchResult
 
-data class UnifiedSearchResults(val success: Boolean, val results: List<SearchResult>)
+typealias ProviderID = String
+
+data class UnifiedSearchResult(val provider: ProviderID, val success: Boolean, val result: SearchResult)
 
 interface IUnifiedSearchRepository {
     fun refresh()
     fun startLoading()
-    fun loadMore(query: String, onResult: (UnifiedSearchResults) -> Unit, onError: (Throwable) -> Unit)
+    fun queryAll(
+        query: String,
+        onResult: (UnifiedSearchResult) -> Unit,
+        onError: (Throwable) -> Unit,
+        onFinished: (Boolean) -> Unit
+    )
 }

+ 11 - 7
src/main/java/com/owncloud/android/ui/unifiedsearch/SearchOnProvidersTask.kt

@@ -22,6 +22,7 @@ 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(
@@ -34,24 +35,27 @@ class SearchOnProvidersTask(
         const val TAG = "SearchOnProvidersTask"
     }
 
-    data class Result(val success: Boolean = false, val searchResults: List<SearchResult> = emptyList())
+    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 = providers
+        val results: List<Pair<String, RemoteOperationResult<SearchResult>>> = providers
             .map { UnifiedSearchRemoteOperation(it, query) }
-            .map { it.execute(client) }
+            .map { Pair(it.provider, it.execute(client)) }
 
-        val success = results.isNotEmpty() && results.any { it.isSuccess }
+        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.isSuccess }}")
-        Log_OC.d(TAG, "Providers successful: ${results.count { !it.isSuccess }}")
+        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.isSuccess }.map { it.resultData }
+                searchResults = results
+                    .filter { it.second.isSuccess }
+                    .map { Pair(it.first, it.second.resultData) }
+                    .toMap()
             )
             else -> Result()
         }

+ 30 - 12
src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchRemoteRepository.kt

@@ -43,30 +43,48 @@ class UnifiedSearchRemoteRepository(
         TODO("Not yet implemented")
     }
 
-    override fun loadMore(
+    override fun queryAll(
         query: String,
-        onResult: (UnifiedSearchResults) -> Unit,
-        onError: (Throwable) -> Unit
+        onResult: (UnifiedSearchResult) -> Unit,
+        onError: (Throwable) -> Unit,
+        onFinished: (Boolean) -> Unit
     ) {
         Log_OC.d(this, "loadMore")
         fetchProviders(
             onResult = { result ->
                 val providerIds = result.providers.map { it.id }
+                var openRequests = providerIds.size
+                var anyError = false
                 val client = clientFactory.createNextcloudClient(currentAccountProvider.user)
-                val task = SearchOnProvidersTask(query, providerIds, client)
-                asyncRunner.postQuickTask(
-                    task = task,
-                    onResult = {
-                        onResult(UnifiedSearchResults(it.success, it.searchResults))
-                    },
-                    onError = onError
-                )
+                providerIds
+                    .forEach { provider ->
+                        val task = SearchOnProviderTask(query, provider, client)
+                        asyncRunner.postQuickTask(
+                            task = task,
+                            onResult = {
+                                openRequests--
+                                anyError = anyError || !it.success
+                                onResult(UnifiedSearchResult(provider, it.success, it.searchResult))
+                                if (openRequests == 0) {
+                                    onFinished(!anyError)
+                                }
+                            },
+                            onError = {
+                                openRequests--
+                                anyError = true
+                                onError(it)
+                                if (openRequests == 0) {
+                                    onFinished(!anyError)
+                                }
+                            }
+                        )
+                    }
             },
             onError = onError
         )
     }
 
-    private fun fetchProviders(onResult: (SearchProviders) -> Unit, onError: (Throwable) -> Unit) {
+    fun fetchProviders(onResult: (SearchProviders) -> Unit, onError: (Throwable) -> Unit) {
         Log_OC.d(this, "fetchProviders")
         if (this.providers != null) {
             onResult(this.providers!!)

+ 29 - 14
src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchViewModel.kt

@@ -47,10 +47,14 @@ class UnifiedSearchViewModel() : ViewModel() {
     private var last: Int = -1
 
     val isLoading: MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)
-    val searchResults = MutableLiveData<MutableList<SearchResult>>(mutableListOf())
+    val searchResults = MutableLiveData<MutableMap<ProviderID, SearchResult>>(mutableMapOf())
     val error: MutableLiveData<String> = MutableLiveData<String>("")
     val query: MutableLiveData<String> = MutableLiveData()
 
+    companion object {
+        private const val TAG = "UnifiedSearchViewModel"
+    }
+
     @Inject
     constructor(
         currentAccountProvider: CurrentAccountProvider,
@@ -74,27 +78,31 @@ class UnifiedSearchViewModel() : ViewModel() {
 
     open fun refresh() {
         last = -1
-        searchResults.value = mutableListOf()
-        loadMore()
+        searchResults.value = mutableMapOf()
+        startLoading(query.value.orEmpty())
     }
 
     open fun startLoading(query: String) {
         if (!loadingStarted) {
             loadingStarted = true
             this.query.value = query
-            loadMore()
+            queryAll()
         }
     }
 
-    open fun loadMore() {
+    fun queryAll() {
         val queryTerm = query.value.orEmpty()
 
         if (isLoading.value != true && queryTerm.isNotBlank()) {
             isLoading.value = true
-            repository.loadMore(queryTerm, this::onSearchResult, this::onError)
+            repository.queryAll(queryTerm, this::onSearchResult, this::onError, this::onSearchFinished)
         }
     }
 
+    open fun loadMore() {
+        // TODO load more results for a single provider
+    }
+
     fun openFile(fileUrl: String) {
         if (isLoading.value == false) {
             isLoading.value = true
@@ -115,23 +123,30 @@ class UnifiedSearchViewModel() : ViewModel() {
     }
 
     fun onError(error: Throwable) {
-        Log_OC.d("Unified Search", "Error: " + error.stackTrace)
+        Log_OC.d(TAG, "Error: " + error.stackTrace)
     }
 
-    fun onSearchResult(result: UnifiedSearchResults) {
+    @Synchronized
+    fun onSearchResult(result: UnifiedSearchResult) {
         isLoading.value = false
 
         if (result.success) {
             // TODO append if already exists
-            searchResults.value = result.results.toMutableList()
-        } else {
-            error.value = resources.getString(R.string.search_error)
+            val currentValues: MutableMap<ProviderID, SearchResult> = searchResults.value ?: mutableMapOf()
+            currentValues.put(result.provider, result.result)
+            searchResults.value = currentValues
         }
 
-        Log_OC.d("Unified Search", "Success: " + result.success)
+        Log_OC.d(TAG, "onSearchResult: Provider '${result.provider}', success: ${result.success}")
         if (result.success) {
-            Log_OC.d("Unified Search", "Got results from ${result.results.size} providers")
-            Log_OC.d("Unified Search", "Total results: " + result.results.sumOf { it.entries.size })
+            Log_OC.d(TAG, "onSearchResult: Provider '${result.provider}', result count: ${result.result.entries.size}")
+        }
+    }
+
+    fun onSearchFinished(success: Boolean) {
+        isLoading.value = false
+        if (!success) {
+            error.value = resources.getString(R.string.search_error)
         }
     }