Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb2eb882 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

Issue 5171: Fix limited search results

parent 49364063
Loading
Loading
Loading
Loading
+56 −23
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ package foundation.e.apps.api.fused
import android.content.Context
import android.text.format.Formatter
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.map
import com.aurora.gplayapi.Constants
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
@@ -176,21 +179,33 @@ class FusedAPIImpl @Inject constructor(
     * Fetches search results from cleanAPK and GPlay servers and returns them
     * @param query Query
     * @param authData [AuthData]
     * @return A list of nullable [FusedApp]
     * @return A livedata list of non-nullable [FusedApp].
     * Observe this livedata to display new apps as they are fetched from the network.
     */
    suspend fun getSearchResults(query: String, authData: AuthData): List<FusedApp> {
        val fusedResponse = mutableListOf<FusedApp>()

    fun getSearchResults(query: String, authData: AuthData): LiveData<List<FusedApp>> {
        /*
         * Returning livedata to improve performance, so that we do not have to wait forever
         * for all results to be fetched from network before showing them.
         * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
         */
        return liveData {
            when (preferenceManagerModule.preferredApplicationType()) {
                APP_TYPE_ANY -> {
                fusedResponse.addAll(getCleanAPKSearchResults(query))
                fusedResponse.addAll(getGplaySearchResults(query, authData))
                    val cleanApkResults = getCleanAPKSearchResults(query)
                    if (cleanApkResults.isNotEmpty()) {
                        /*
                         * If cleanapk results are empty, dont emit emit data as it may
                         * briefly show "No apps found..."
                         */
                        emit(cleanApkResults)
                    }
                    emitSource(getGplayAndCleanapkCombinedResults(query, authData, cleanApkResults))
                }
                APP_TYPE_OPEN -> {
                fusedResponse.addAll(getCleanAPKSearchResults(query))
                    emit(getCleanAPKSearchResults(query))
                }
                APP_TYPE_PWA -> {
                fusedResponse.addAll(
                    emit(
                        getCleanAPKSearchResults(
                            query,
                            CleanAPKInterface.APP_SOURCE_ANY,
@@ -199,7 +214,8 @@ class FusedAPIImpl @Inject constructor(
                    )
                }
            }
        return fusedResponse.distinctBy { it.package_name }
        }

    }

    suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> {
@@ -589,10 +605,27 @@ class FusedAPIImpl @Inject constructor(
        return list
    }

    private suspend fun getGplaySearchResults(query: String, authData: AuthData): List<FusedApp> {
    /*
     * Function to return a livedata with value from cleanapk and Google Play store combined.
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
     */
    private fun getGplayAndCleanapkCombinedResults(
        query: String,
        authData: AuthData,
        cleanApkResults: List<FusedApp>
    ): LiveData<List<FusedApp>> {
        val localList = ArrayList<FusedApp>(cleanApkResults)
        return getGplaySearchResults(query, authData).map { list ->
            localList.apply {
                addAll(list)
            }.distinctBy { it.package_name }
        }
    }

    private fun getGplaySearchResults(query: String, authData: AuthData): LiveData<List<FusedApp>> {
        val searchResults = gPlayAPIRepository.getSearchResults(query, authData)
        return searchResults.map { app ->
            app.transformToFusedApp()
        return searchResults.map {
            it.map { app -> app.transformToFusedApp() }
        }
    }

+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

package foundation.e.apps.api.fused

import androidx.lifecycle.LiveData
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
@@ -91,7 +92,7 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.fetchAuthData(email, aasToken)
    }

    suspend fun getSearchResults(query: String, authData: AuthData): List<FusedApp> {
    fun getSearchResults(query: String, authData: AuthData): LiveData<List<FusedApp>> {
        return fusedAPIImpl.getSearchResults(query, authData)
    }

+28 −18
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@
package foundation.e.apps.api.gplay

import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.liveData
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
@@ -82,29 +84,37 @@ class GPlayAPIImpl @Inject constructor(
        return searchData.filter { it.suggestedQuery.isNotBlank() }
    }

    suspend fun getSearchResults(query: String, authData: AuthData): List<App> {
        val searchData = mutableListOf<App>()
    fun getSearchResults(query: String, authData: AuthData): LiveData<List<App>> {
        /*
         * Send livedata to improve UI performance, so we don't have to wait for loading all results.
         * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
         */
        return liveData {
            withContext(Dispatchers.IO) {
                /*
                 * Variable names and logic made same as that of Aurora store.
                 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
                 */
                val searchHelper = SearchHelper(authData).using(gPlayHttpClient)
            val searchResult = searchHelper.searchResults(query)
            searchData.addAll(searchResult.appList)
                val searchBundle = searchHelper.searchResults(query)

                emit(searchBundle.appList)

            // Fetch more results in case the given result is a promoted app
            if (searchData.size == 1) {
                val bundleSet: MutableSet<SearchBundle.SubBundle> = searchResult.subBundles
                var nextSubBundleSet: MutableSet<SearchBundle.SubBundle>
                do {
                    val searchBundle = searchHelper.next(bundleSet)
                    if (searchBundle.appList.isNotEmpty()) {
                        searchData.addAll(searchBundle.appList)
                    nextSubBundleSet = searchBundle.subBundles
                    val newSearchBundle = searchHelper.next(nextSubBundleSet)
                    if (newSearchBundle.appList.isNotEmpty()) {
                        searchBundle.apply {
                            subBundles.clear()
                            subBundles.addAll(newSearchBundle.subBundles)
                            appList.addAll(newSearchBundle.appList)
                            emit(searchBundle.appList)
                        }
                    bundleSet.apply {
                        clear()
                        addAll(searchBundle.subBundles)
                    }
                } while (bundleSet.isNotEmpty())
                } while (nextSubBundleSet.isNotEmpty())
            }
        }
        return searchData
    }

    suspend fun getDownloadInfo(
+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

package foundation.e.apps.api.gplay

import androidx.lifecycle.LiveData
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
@@ -46,7 +47,7 @@ class GPlayAPIRepository @Inject constructor(
        return gPlayAPIImpl.getSearchSuggestions(query, authData)
    }

    suspend fun getSearchResults(query: String, authData: AuthData): List<App> {
    fun getSearchResults(query: String, authData: AuthData): LiveData<List<App>> {
        return gPlayAPIImpl.getSearchResults(query, authData)
    }

+15 −2
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ class SearchFragment :
    private val appProgressViewModel: AppProgressViewModel by viewModels()

    private val SUGGESTION_KEY = "suggestion"
    private var lastSearch = ""

    private var searchView: SearchView? = null
    private var shimmerLayout: ShimmerFrameLayout? = null
@@ -193,7 +194,19 @@ class SearchFragment :
            }
            listAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
                override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                    recyclerView!!.scrollToPosition(0)
                    searchView?.run {
                        /*
                         * Only scroll back to 0 position for a new search.
                         *
                         * If we are getting new results from livedata for the old search query,
                         * do not scroll to top as the user may be scrolling to see already
                         * populated results.
                         */
                        if (lastSearch != query?.toString()) {
                            recyclerView?.scrollToPosition(0)
                            lastSearch = query.toString()
                        }
                    }
                }
            })
        }
@@ -218,7 +231,7 @@ class SearchFragment :
            shimmerLayout?.visibility = View.VISIBLE
            recyclerView?.visibility = View.GONE
            noAppsFoundLayout?.visibility = View.GONE
            mainActivityViewModel.authData.value?.let { searchViewModel.getSearchResults(text, it) }
            mainActivityViewModel.authData.value?.let { searchViewModel.getSearchResults(text, it, this) }
        }
        return false
    }
Loading