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

Commit 9707a29f authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

Merge branch '5171-fix_limited_search_results' into 'main'

Issue 5171: Fix limited search results

See merge request !133
parents 49364063 fb2eb882
Loading
Loading
Loading
Loading
Loading
+56 −23
Original line number Original line Diff line number Diff line
@@ -21,6 +21,9 @@ package foundation.e.apps.api.fused
import android.content.Context
import android.content.Context
import android.text.format.Formatter
import android.text.format.Formatter
import android.util.Log
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.Constants
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
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
     * Fetches search results from cleanAPK and GPlay servers and returns them
     * @param query Query
     * @param query Query
     * @param authData [AuthData]
     * @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> {
    fun getSearchResults(query: String, authData: AuthData): LiveData<List<FusedApp>> {
        val fusedResponse = mutableListOf<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()) {
            when (preferenceManagerModule.preferredApplicationType()) {
                APP_TYPE_ANY -> {
                APP_TYPE_ANY -> {
                fusedResponse.addAll(getCleanAPKSearchResults(query))
                    val cleanApkResults = getCleanAPKSearchResults(query)
                fusedResponse.addAll(getGplaySearchResults(query, authData))
                    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 -> {
                APP_TYPE_OPEN -> {
                fusedResponse.addAll(getCleanAPKSearchResults(query))
                    emit(getCleanAPKSearchResults(query))
                }
                }
                APP_TYPE_PWA -> {
                APP_TYPE_PWA -> {
                fusedResponse.addAll(
                    emit(
                        getCleanAPKSearchResults(
                        getCleanAPKSearchResults(
                            query,
                            query,
                            CleanAPKInterface.APP_SOURCE_ANY,
                            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> {
    suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> {
@@ -589,10 +605,27 @@ class FusedAPIImpl @Inject constructor(
        return list
        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)
        val searchResults = gPlayAPIRepository.getSearchResults(query, authData)
        return searchResults.map { app ->
        return searchResults.map {
            app.transformToFusedApp()
            it.map { app -> app.transformToFusedApp() }
        }
        }
    }
    }


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


package foundation.e.apps.api.fused
package foundation.e.apps.api.fused


import androidx.lifecycle.LiveData
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.Category
@@ -91,7 +92,7 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.fetchAuthData(email, aasToken)
        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)
        return fusedAPIImpl.getSearchResults(query, authData)
    }
    }


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


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


    suspend fun getSearchResults(query: String, authData: AuthData): List<App> {
    fun getSearchResults(query: String, authData: AuthData): LiveData<List<App>> {
        val searchData = mutableListOf<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) {
            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 searchHelper = SearchHelper(authData).using(gPlayHttpClient)
            val searchResult = searchHelper.searchResults(query)
                val searchBundle = searchHelper.searchResults(query)
            searchData.addAll(searchResult.appList)

                emit(searchBundle.appList)


            // Fetch more results in case the given result is a promoted app
                var nextSubBundleSet: MutableSet<SearchBundle.SubBundle>
            if (searchData.size == 1) {
                val bundleSet: MutableSet<SearchBundle.SubBundle> = searchResult.subBundles
                do {
                do {
                    val searchBundle = searchHelper.next(bundleSet)
                    nextSubBundleSet = searchBundle.subBundles
                    if (searchBundle.appList.isNotEmpty()) {
                    val newSearchBundle = searchHelper.next(nextSubBundleSet)
                        searchData.addAll(searchBundle.appList)
                    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(
    suspend fun getDownloadInfo(
+2 −1
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@


package foundation.e.apps.api.gplay
package foundation.e.apps.api.gplay


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


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


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


    private var searchView: SearchView? = null
    private var searchView: SearchView? = null
    private var shimmerLayout: ShimmerFrameLayout? = null
    private var shimmerLayout: ShimmerFrameLayout? = null
@@ -193,7 +194,19 @@ class SearchFragment :
            }
            }
            listAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            listAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
                override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                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
            shimmerLayout?.visibility = View.VISIBLE
            recyclerView?.visibility = View.GONE
            recyclerView?.visibility = View.GONE
            noAppsFoundLayout?.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
        return false
    }
    }
Loading