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

Commit 9b412961 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

Merge branch '5131-fix_limited_apps' into 'main'

Get more apps for different categories, fix limited apps in search

See merge request !99
parents 9c424dd6 a9e5c8f9
Loading
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -227,6 +227,16 @@ class FusedAPIImpl @Inject constructor(
        }
    }

    suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List<String> {
        return gPlayAPIRepository.listAppCategoryUrls(browseUrl, authData)
    }

    suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair<List<FusedApp>, String> {
        return gPlayAPIRepository.getAppsAndNextClusterUrl(browseUrl, authData).let {
            Pair(it.first.map { app -> app.transformToFusedApp() }, it.second)
        }
    }

    suspend fun getApplicationDetails(
        packageNameList: List<String>,
        authData: AuthData,
+8 −0
Original line number Diff line number Diff line
@@ -94,6 +94,14 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.listApps(category, browseUrl, authData)
    }

    suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List<String> {
        return fusedAPIImpl.getPlayStoreAppCategoryUrls(browseUrl, authData)
    }

    suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair<List<FusedApp>, String> {
        return fusedAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData)
    }

    suspend fun getAppsListBasedOnCategory(
        category: String,
        browseUrl: String,
+131 −11
Original line number Diff line number Diff line
@@ -19,21 +19,20 @@
package foundation.e.apps.api.gplay

import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.data.models.*
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.AuthValidator
import com.aurora.gplayapi.helpers.CategoryHelper
import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.gplayapi.helpers.SearchHelper
import com.aurora.gplayapi.helpers.StreamHelper
import com.aurora.gplayapi.helpers.TopChartsHelper
import foundation.e.apps.api.gplay.token.TokenRepository
import foundation.e.apps.api.gplay.utils.GPlayHttpClient
import foundation.e.apps.utils.modules.DataStoreModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import java.util.Locale
import javax.inject.Inject
@@ -84,9 +83,18 @@ class GPlayAPIImpl @Inject constructor(

            // Fetch more results in case the given result is a promoted app
            if (searchData.size == 1) {
                val searchBundle = searchHelper.next(searchResult.subBundles)
                val bundleSet: MutableSet<SearchBundle.SubBundle> = searchResult.subBundles
                do {
                    val searchBundle = searchHelper.next(bundleSet)
                    if (searchBundle.appList.isNotEmpty()) {
                        searchData.addAll(searchBundle.appList)
                    }
                    bundleSet.apply {
                        clear()
                        addAll(searchBundle.subBundles)
                    }
                } while (bundleSet.isNotEmpty())
            }
        }
        return searchData
    }
@@ -145,14 +153,126 @@ class GPlayAPIImpl @Inject constructor(
        return categoryList
    }

    /**
     * Get list of "clusterBrowseUrl" which can be used to get [StreamCluster] objects which
     * have "clusterNextPageUrl" to get subsequent [StreamCluster] objects.
     *
     * * -- browseUrl
     *    |
     *    StreamBundle 1 (streamNextPageUrl points to StreamBundle 2)
     *        clusterBrowseUrl 1 -> clusterNextPageUrl 1.1 -> clusterNextPageUrl -> 1.2 ....
     *        clusterBrowseUrl 2 -> clusterNextPageUrl 2.1 -> clusterNextPageUrl -> 2.2 ....
     *        clusterBrowseUrl 3 -> clusterNextPageUrl 3.1 -> clusterNextPageUrl -> 3.2 ....
     *    StreamBundle 2
     *        clusterBroseUrl 4 -> ...
     *        clusterBroseUrl 5 -> ...
     *
     * This function returns the clusterBrowseUrls 1,2,3,4,5...
     */
    suspend fun listAppCategoryUrls(browseUrl: String, authData: AuthData): List<String> {
        val urlList = mutableListOf<String>()

        withContext(Dispatchers.IO) {
            supervisorScope {

                val categoryHelper = CategoryHelper(authData).using(gPlayHttpClient)

                var streamBundle: StreamBundle
                var nextStreamBundleUrl = browseUrl

                do {
                    streamBundle = categoryHelper.getSubCategoryBundle(nextStreamBundleUrl)
                    val streamClusters = streamBundle.streamClusters.values

                    urlList.addAll(streamClusters.map { it.clusterBrowseUrl })
                    nextStreamBundleUrl = streamBundle.streamNextPageUrl

                } while (nextStreamBundleUrl.isNotBlank())
            }
        }

        return urlList.distinct().filter { it.isNotBlank() }
    }

    /**
     * Accept a [browseUrl] of type "clusterBrowseUrl" or "clusterNextPageUrl".
     * Fetch a StreamCluster from the [browseUrl] and return pair of:
     * - List od apps to display.
     * - String url "clusterNextPageUrl" pointing to next StreamCluster. This can be blank (not null).
     */
    suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair<List<App>, String> {
        val streamCluster: StreamCluster
        withContext(Dispatchers.IO) {
            supervisorScope {
                val streamHelper = StreamHelper(authData).using(gPlayHttpClient)
                val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl)

                streamCluster = if (browseResponse.contentsUrl.isNotEmpty()) {
                    streamHelper.getNextStreamCluster(browseResponse.contentsUrl)
                } else if (browseResponse.hasBrowseTab()) {
                    streamHelper.getNextStreamCluster(browseResponse.browseTab.listUrl)
                } else {
                    StreamCluster()
                }
            }
        }
        return Pair(streamCluster.clusterAppList, streamCluster.clusterNextPageUrl)
    }

    suspend fun listApps(browseUrl: String, authData: AuthData): List<App> {
        val list = mutableListOf<App>()
        withContext(Dispatchers.IO) {
            supervisorScope {
                val categoryHelper = CategoryHelper(authData).using(gPlayHttpClient)
            val streamClusters = categoryHelper.getSubCategoryBundle(browseUrl).streamClusters

                var streamBundle: StreamBundle
                var nextStreamBundleUrl = browseUrl

                /*
                 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131
                 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171
                 *
                 * Logic: We start with the browseUrl.
                 * When we call getSubCategoryBundle(), we get a new StreamBundle object, having
                 * StreamClusters, which have app data.
                 * The generated StreamBundle also has a url for next StreamBundle to be generated
                 * with fresh app data.
                 * Hence we loop as long as the StreamBundle's next page url is not blank.
                 */
                do {
                    streamBundle = categoryHelper.getSubCategoryBundle(nextStreamBundleUrl)
                    val streamClusters = streamBundle.streamClusters

                    /*
                     * Similarly to the logic of StreamBundles, each StreamCluster can have a url,
                     * pointing to another StreamCluster with new set of app data.
                     * We loop over all the StreamCluster of one StreamBundle, and for each of the
                     * StreamCluster we continue looping as long as the StreamCluster.clusterNextPageUrl
                     * is not blank.
                     */
                    streamClusters.values.forEach { streamCluster ->
                        list.addAll(streamCluster.clusterAppList)   // Add all apps for this StreamCluster

                        // Loop over possible next StreamClusters
                        var currentStreamCluster = streamCluster
                        while (currentStreamCluster.hasNext()) {
                            currentStreamCluster = categoryHelper
                                .getNextStreamCluster(currentStreamCluster.clusterNextPageUrl)
                                .also {
                                    list.addAll(it.clusterAppList)
                                }
                        }
                    }

                    nextStreamBundleUrl = streamBundle.streamNextPageUrl

                } while (streamBundle.hasNext())

                // TODO: DEAL WITH DUPLICATE AND LESS ITEMS
                /*val streamClusters = categoryHelper.getSubCategoryBundle(browseUrl).streamClusters
                streamClusters.values.forEach {
                    list.addAll(it.clusterAppList)
                }*/
            }
        }
        return list.distinctBy { it.packageName }
+8 −0
Original line number Diff line number Diff line
@@ -82,4 +82,12 @@ class GPlayAPIRepository @Inject constructor(
    suspend fun listApps(browseUrl: String, authData: AuthData): List<App> {
        return gPlayAPIImpl.listApps(browseUrl, authData)
    }

    suspend fun listAppCategoryUrls(browseUrl: String, authData: AuthData): List<String> {
        return gPlayAPIImpl.listAppCategoryUrls(browseUrl, authData)
    }

    suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair<List<App>, String> {
        return gPlayAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData)
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -163,6 +163,21 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
                        authData,
                        args.source
                    )

                    if (args.source != "Open Source" && args.source != "PWA") {
                        /*
                         * For Play store apps we try to load more apps on reaching end of list.
                         * Source: https://stackoverflow.com/a/46342525
                         */
                        recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
                            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                                super.onScrollStateChanged(recyclerView, newState)
                                if (!recyclerView.canScrollVertically(1)) {
                                    viewModel.getPlayStoreAppsOnScroll(args.browseUrl, authData)
                                }
                            }
                        })
                    }
                }
            }
        }
Loading