From 2b5a9f9ed0691a5dcd1e3982c9b1fe4fd7305a85 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 19:42:16 +0530 Subject: [PATCH 01/35] issue_5131_2: Comment out some unused listApps() methods. --- .../main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 5 +++-- .../java/foundation/e/apps/api/fused/FusedAPIRepository.kt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index aac88fa67..6a2d7b767 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -273,7 +273,8 @@ class FusedAPIImpl @Inject constructor( fusedDownload.downloadURLList = list } - suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { + // This method is no longer used + /*suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() if (preferredApplicationType != "any") { @@ -293,7 +294,7 @@ class FusedAPIImpl @Inject constructor( app.transformToFusedApp() } } - } + }*/ suspend fun getPWAApps(category: String): Pair, ResultStatus> { var list = mutableListOf() diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index f92123cc0..8711b51c0 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -100,9 +100,10 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getSearchResults(query, authData) } - suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { + // This method is no longer used + /*suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { return fusedAPIImpl.listApps(category, browseUrl, authData) - } + }*/ suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List { return fusedAPIImpl.getPlayStoreAppCategoryUrls(browseUrl, authData) -- GitLab From 3206b287a7ecff93a2f074e80cfaf12d979e4ab7 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 3 Jun 2022 18:06:48 +0530 Subject: [PATCH 02/35] issue_5131_2 [WIP]: Comment out loading apps on scroll end --- .../applicationlist/ApplicationListFragment.kt | 8 ++++---- .../applicationlist/ApplicationListViewModel.kt | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 258862e53..6281e2be9 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -208,11 +208,11 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li args.source ) - if (args.source != "Open Source" && args.source != "PWA") { - /* + /*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 - */ + *//* binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) @@ -221,7 +221,7 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li } } }) - } + }*/ appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { updateProgressOfDownloadingItems(binding.recyclerView, it) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 72a518e18..e257aa3ca 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -45,12 +45,12 @@ class ApplicationListViewModel @Inject constructor( private var nextClusterUrl = String() - fun getPlayStoreAppsOnScroll(browseUrl: String, authData: AuthData) { + /*fun getPlayStoreAppsOnScroll(browseUrl: String, authData: AuthData) { viewModelScope.launch { - /* + *//* * Init condition. * If category urls are empty or browseUrl has changed, get new category urls. - */ + *//* if (playStoreCategoryUrls.isEmpty() || browseUrl != lastBrowseUrl) { categoryUrlsPointer = 0 playStoreCategoryUrls.clear() @@ -62,15 +62,15 @@ class ApplicationListViewModel @Inject constructor( ) } - /* + *//* * This is the new list that will be set to the adapter. * Add existing apps now and add additional apps later. - */ + *//* val newList = mutableListOf().apply { appListLiveData.value?.first?.let { addAll(it) } } - /** + *//** * There are four types of urls we are dealing with here. * - "browseUrl": looks like: homeV2?cat=SOCIAL&c=3 * - "category urls" or "clusterBrowseUrl": @@ -116,7 +116,7 @@ class ApplicationListViewModel @Inject constructor( * Case 4: All the above cases do not run. This means all available data has been fetched. * * [nextClusterUrl] can thus take value of "clusterBrowseUrl" as well as "clusterNextPageUrl" - */ + *//* if (nextClusterUrl.isBlank()) { nextClusterUrl = playStoreCategoryUrls.getOrElse(categoryUrlsPointer++) { String() } } @@ -130,7 +130,7 @@ class ApplicationListViewModel @Inject constructor( } } } - } + }*/ fun getList(category: String, browseUrl: String, authData: AuthData, source: String) { if (appListLiveData.value?.first?.isNotEmpty() == true) { -- GitLab From ead231605d33b002bcb1480c14e7fa79d2792ced Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Fri, 3 Jun 2022 21:25:29 +0530 Subject: [PATCH 03/35] issue_5131_2 [WIP]: Create GPlayAPIImpl.getNextStreamBundle(), declare them in repositories and in ApplicationListViewModel --- .../e/apps/api/fused/FusedAPIImpl.kt | 9 +++++++ .../e/apps/api/fused/FusedAPIRepository.kt | 9 +++++++ .../e/apps/api/gplay/GPlayAPIImpl.kt | 25 +++++++++++++++++++ .../e/apps/api/gplay/GPlayAPIRepository.kt | 9 +++++++ .../ApplicationListViewModel.kt | 12 +++++++++ 5 files changed, 64 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 6a2d7b767..7ab40bacb 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -27,6 +27,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Artwork import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category +import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.helpers.TopChartsHelper import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R @@ -322,6 +323,14 @@ class FusedAPIImpl @Inject constructor( return Pair(list, status) } + suspend fun getNextStreamBundle( + authData: AuthData, + homeUrl: String, + currentStreamBundle: StreamBundle, + ): StreamBundle { + return gPlayAPIRepository.getNextStreamBundle(authData, homeUrl, currentStreamBundle) + } + suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): Pair, ResultStatus> { var list = mutableListOf() val status = runCodeBlockWithTimeout({ diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 8711b51c0..f2666f24e 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -21,6 +21,7 @@ package foundation.e.apps.api.fused import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category +import com.aurora.gplayapi.data.models.StreamBundle import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome @@ -113,6 +114,14 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData) } + suspend fun getNextStreamBundle( + authData: AuthData, + homeUrl: String, + currentStreamBundle: StreamBundle, + ): StreamBundle { + return fusedAPIImpl.getNextStreamBundle(authData, homeUrl, currentStreamBundle) + } + suspend fun getAppsListBasedOnCategory( category: String, browseUrl: String, diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index e4b8fc65e..dc9147dae 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -237,6 +237,31 @@ class GPlayAPIImpl @Inject constructor( return Pair(streamCluster.clusterAppList, streamCluster.clusterNextPageUrl) } + /* + * Get StreamBundle, either from the homeUrl of a category, + * or from the current StreamBundle's next url. + * + * This function will also be used to fetch the next StreamBundle after + * all StreamCluster's in the current StreamBundle is iterated over. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + suspend fun getNextStreamBundle( + authData: AuthData, + homeUrl: String, + currentStreamBundle: StreamBundle, + ): StreamBundle { + return withContext(Dispatchers.IO) { + val categoryHelper = CategoryHelper(authData).using(gPlayHttpClient) + if (currentStreamBundle.streamClusters.isEmpty()) { + categoryHelper.getSubCategoryBundle(homeUrl) + } + else { + categoryHelper.getSubCategoryBundle(currentStreamBundle.streamNextPageUrl) + } + } + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { val list = mutableListOf() withContext(Dispatchers.IO) { diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index 18f37ba54..b89346192 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -23,6 +23,7 @@ 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.StreamBundle import com.aurora.gplayapi.helpers.TopChartsHelper import javax.inject.Inject @@ -79,6 +80,14 @@ class GPlayAPIRepository @Inject constructor( return gPlayAPIImpl.getCategoriesList(type, authData) } + suspend fun getNextStreamBundle( + authData: AuthData, + homeUrl: String, + currentStreamBundle: StreamBundle, + ): StreamBundle { + return gPlayAPIImpl.getNextStreamBundle(authData, homeUrl, currentStreamBundle) + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { return gPlayAPIImpl.listApps(browseUrl, authData) } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index e257aa3ca..c0bd1fce3 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -22,6 +22,8 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.StreamBundle +import com.aurora.gplayapi.data.models.StreamCluster import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp @@ -45,6 +47,9 @@ class ApplicationListViewModel @Inject constructor( private var nextClusterUrl = String() + private var streamBundle = StreamBundle() + private var streamCluster = StreamCluster() + /*fun getPlayStoreAppsOnScroll(browseUrl: String, authData: AuthData) { viewModelScope.launch { *//* @@ -170,6 +175,13 @@ class ApplicationListViewModel @Inject constructor( } } + private suspend fun getNextStreamBundle( + browseUrl: String, + authData: AuthData, + ) { + streamBundle = fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle) + } + private fun getOrigin(source: String) = if (source.contentEquals("Open Source")) Origin.CLEANAPK else Origin.GPLAY } -- GitLab From e58bea54efef71726ad444a5947db396efbb73b6 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 13:24:45 +0530 Subject: [PATCH 04/35] issue_5131_2 [WIP]: Create GPlayAPIImpl.getAdjustedFirstCluster(), declare them in repositories and in ApplicationListViewModel --- .../e/apps/api/fused/FusedAPIImpl.kt | 8 ++++ .../e/apps/api/fused/FusedAPIRepository.kt | 8 ++++ .../e/apps/api/gplay/GPlayAPIImpl.kt | 43 +++++++++++++++++++ .../e/apps/api/gplay/GPlayAPIRepository.kt | 8 ++++ .../ApplicationListViewModel.kt | 11 +++++ 5 files changed, 78 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 7ab40bacb..5882cbe80 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -28,6 +28,7 @@ import com.aurora.gplayapi.data.models.Artwork import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.StreamBundle +import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.TopChartsHelper import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R @@ -331,6 +332,13 @@ class FusedAPIImpl @Inject constructor( return gPlayAPIRepository.getNextStreamBundle(authData, homeUrl, currentStreamBundle) } + suspend fun getAdjustedFirstCluster( + authData: AuthData, + streamBundle: StreamBundle, + ): StreamCluster { + return gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle) + } + suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): Pair, ResultStatus> { var list = mutableListOf() val status = runCodeBlockWithTimeout({ diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index f2666f24e..c244c8c4c 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -22,6 +22,7 @@ import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.StreamBundle +import com.aurora.gplayapi.data.models.StreamCluster import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome @@ -122,6 +123,13 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getNextStreamBundle(authData, homeUrl, currentStreamBundle) } + suspend fun getAdjustedFirstCluster( + authData: AuthData, + streamBundle: StreamBundle, + ): StreamCluster { + return fusedAPIImpl.getAdjustedFirstCluster(authData, streamBundle) + } + suspend fun getAppsListBasedOnCategory( category: String, browseUrl: String, diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index dc9147dae..64ffff021 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -262,6 +262,49 @@ class GPlayAPIImpl @Inject constructor( } } + /* + * Get first StreamCluster of a StreamBundle. + * Ideally it would just be streamBundle.streamClusters[0], but in case the zeroth StreamCluster + * does not have a next url, we need to get a StreamCluster which has a clusterNextPageUrl. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + suspend fun getAdjustedFirstCluster( + authData: AuthData, + streamBundle: StreamBundle, + ): StreamCluster { + return withContext(Dispatchers.IO) { + if (streamBundle.streamClusters.isNotEmpty()) { + streamBundle.streamClusters[0]?.run { + if (hasNext()) { + /* + * If zeroth StreamCluster's next url is not blank, return it. + */ + return@withContext this + } else { + /* + * Try fetching a StreamCluster whose next url is not blank. + * Logic taken from Aurora Store code. + */ + val streamHelper = StreamHelper(authData).using(gPlayHttpClient) + val browseResponse = streamHelper.getBrowseStreamResponse(this.clusterBrowseUrl) + if (browseResponse.contentsUrl.isNotEmpty()) { + return@withContext streamHelper.getNextStreamCluster(browseResponse.contentsUrl) + } + else if (browseResponse.hasBrowseTab()) { + return@withContext streamHelper.getNextStreamCluster(browseResponse.browseTab.listUrl) + } + } + } + } + + /* + * If nothing works return blank StreamCluster. + */ + StreamCluster() + } + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { val list = mutableListOf() withContext(Dispatchers.IO) { diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index b89346192..e8699d71d 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -24,6 +24,7 @@ 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.StreamBundle +import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.TopChartsHelper import javax.inject.Inject @@ -88,6 +89,13 @@ class GPlayAPIRepository @Inject constructor( return gPlayAPIImpl.getNextStreamBundle(authData, homeUrl, currentStreamBundle) } + suspend fun getAdjustedFirstCluster( + authData: AuthData, + streamBundle: StreamBundle, + ): StreamCluster { + return gPlayAPIImpl.getAdjustedFirstCluster(authData, streamBundle) + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { return gPlayAPIImpl.listApps(browseUrl, authData) } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index c0bd1fce3..9dc71411f 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -182,6 +182,17 @@ class ApplicationListViewModel @Inject constructor( streamBundle = fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle) } + private suspend fun getAdjustedFirstCluster( + authData: AuthData, + streamBundle: StreamBundle, + ) { + fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle).run { + streamCluster.clusterAppList.addAll(this.clusterAppList) + streamCluster.clusterNextPageUrl = this.clusterNextPageUrl + streamCluster.clusterBrowseUrl = this.clusterBrowseUrl + } + } + private fun getOrigin(source: String) = if (source.contentEquals("Open Source")) Origin.CLEANAPK else Origin.GPLAY } -- GitLab From 1f7967965698dc37681ca20b59bab821141175fc Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 15:31:30 +0530 Subject: [PATCH 05/35] issue_5131_2 [WIP]: Create GPlayAPIImpl.getNextStreamCluster(), declare them in repositories and in ApplicationListViewModel --- .../e/apps/api/fused/FusedAPIImpl.kt | 7 +++++++ .../e/apps/api/fused/FusedAPIRepository.kt | 7 +++++++ .../e/apps/api/gplay/GPlayAPIImpl.kt | 20 +++++++++++++++++++ .../e/apps/api/gplay/GPlayAPIRepository.kt | 7 +++++++ .../ApplicationListViewModel.kt | 10 ++++++++++ 5 files changed, 51 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 5882cbe80..78d845faf 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -339,6 +339,13 @@ class FusedAPIImpl @Inject constructor( return gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle) } + suspend fun getNextStreamCluster( + authData: AuthData, + currentStreamCluster: StreamCluster, + ): StreamCluster { + return gPlayAPIRepository.getNextStreamCluster(authData, currentStreamCluster) + } + suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): Pair, ResultStatus> { var list = mutableListOf() val status = runCodeBlockWithTimeout({ diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index c244c8c4c..f4f10c433 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -130,6 +130,13 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getAdjustedFirstCluster(authData, streamBundle) } + suspend fun getNextStreamCluster( + authData: AuthData, + currentStreamCluster: StreamCluster, + ): StreamCluster { + return fusedAPIImpl.getNextStreamCluster(authData, currentStreamCluster) + } + suspend fun getAppsListBasedOnCategory( category: String, browseUrl: String, diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index 64ffff021..dc9918dfe 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -305,6 +305,26 @@ class GPlayAPIImpl @Inject constructor( } } + /* + * Get next StreamCluster from currentNextPageUrl. + * This method is to be called when the scrollview reaches the bottom. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + suspend fun getNextStreamCluster( + authData: AuthData, + currentStreamCluster: StreamCluster, + ): StreamCluster { + return withContext(Dispatchers.IO) { + if (currentStreamCluster.hasNext()) { + val streamHelper = StreamHelper(authData).using(gPlayHttpClient) + streamHelper.getNextStreamCluster(currentStreamCluster.clusterNextPageUrl) + } else { + StreamCluster() + } + } + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { val list = mutableListOf() withContext(Dispatchers.IO) { diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index e8699d71d..19a83771b 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -96,6 +96,13 @@ class GPlayAPIRepository @Inject constructor( return gPlayAPIImpl.getAdjustedFirstCluster(authData, streamBundle) } + suspend fun getNextStreamCluster( + authData: AuthData, + currentStreamCluster: StreamCluster, + ): StreamCluster { + return gPlayAPIImpl.getNextStreamCluster(authData, currentStreamCluster) + } + suspend fun listApps(browseUrl: String, authData: AuthData): List { return gPlayAPIImpl.listApps(browseUrl, authData) } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 9dc71411f..de3560242 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -193,6 +193,16 @@ class ApplicationListViewModel @Inject constructor( } } + private suspend fun getNextStreamCluster( + authData: AuthData, + ) { + fusedAPIRepository.getNextStreamCluster(authData, streamCluster).run { + streamCluster.clusterAppList.addAll(this.clusterAppList) + streamCluster.clusterNextPageUrl = this.clusterNextPageUrl + streamCluster.clusterBrowseUrl = this.clusterBrowseUrl + } + } + private fun getOrigin(source: String) = if (source.contentEquals("Open Source")) Origin.CLEANAPK else Origin.GPLAY } -- GitLab From c16fab077edcdeda5806cc494bf738b50cd55bd2 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 15:45:43 +0530 Subject: [PATCH 06/35] issue_5131_2 [WIP]: minor refactoring --- .../apps/applicationlist/ApplicationListViewModel.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index de3560242..381233f25 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -184,12 +184,9 @@ class ApplicationListViewModel @Inject constructor( private suspend fun getAdjustedFirstCluster( authData: AuthData, - streamBundle: StreamBundle, ) { fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle).run { - streamCluster.clusterAppList.addAll(this.clusterAppList) - streamCluster.clusterNextPageUrl = this.clusterNextPageUrl - streamCluster.clusterBrowseUrl = this.clusterBrowseUrl + addNewClusterData(this) } } @@ -197,6 +194,12 @@ class ApplicationListViewModel @Inject constructor( authData: AuthData, ) { fusedAPIRepository.getNextStreamCluster(authData, streamCluster).run { + addNewClusterData(this) + } + } + + private fun addNewClusterData(newCluster: StreamCluster) { + newCluster.run { streamCluster.clusterAppList.addAll(this.clusterAppList) streamCluster.clusterNextPageUrl = this.clusterNextPageUrl streamCluster.clusterBrowseUrl = this.clusterBrowseUrl -- GitLab From 62ef53fc3f5e9a8a66e7fad3f6d87367cb48ce9d Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 16:21:26 +0530 Subject: [PATCH 07/35] issue_5131_2 [WIP]: expose FusedApiImpl App.tranformToFusedApp() via a method mapToFusedApp(app) --- .../main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 2 ++ .../java/foundation/e/apps/api/fused/FusedAPIRepository.kt | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 78d845faf..9a93b9390 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -953,6 +953,8 @@ class FusedAPIImpl @Inject constructor( return app } + fun mapToFusedApp(app: App): FusedApp = app.transformToFusedApp() + /** * Get fused app installation status. * Applicable for both native apps and PWAs. diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index f4f10c433..49076de8c 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -23,6 +23,7 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster +import com.aurora.gplayapi.data.models.App import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome @@ -137,6 +138,10 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getNextStreamCluster(authData, currentStreamCluster) } + fun mapToFusedApp(app: App): FusedApp { + return fusedAPIImpl.mapToFusedApp(app) + } + suspend fun getAppsListBasedOnCategory( category: String, browseUrl: String, -- GitLab From f321b4d2f2d78510f74def256424bbdd3fca4ad4 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Mon, 6 Jun 2022 17:22:35 +0530 Subject: [PATCH 08/35] issue_5131_2 [WIP]: create pointers hasNextStreamBundle, hasNextStreamCluster. Create method getNextDataSet(). Removed old variables and documentation. Tons of new documentation. --- .../ApplicationListViewModel.kt | 194 +++++++++++++----- 1 file changed, 139 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 381233f25..906d4d02c 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -40,16 +40,37 @@ class ApplicationListViewModel @Inject constructor( val appListLiveData: MutableLiveData, ResultStatus?>> = MutableLiveData() - private var lastBrowseUrl = String() - - private val playStoreCategoryUrls = mutableListOf() - private var categoryUrlsPointer = 0 - - private var nextClusterUrl = String() - private var streamBundle = StreamBundle() private var streamCluster = StreamCluster() + /** + * Variable denoting if we can call [getNextStreamCluster] to get a new StreamBundle. + * + * Initially set to true, so that we can get the first StreamBundle. + * Once the first StreamBundle is fetched, this variable value is same + * as [streamBundle].hasNext(). + * + * For more explanation on how [streamBundle] and [streamCluster] work, look at the + * documentation in [getNextDataSet]. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + private var hasNextStreamBundle = true + + /** + * Variable denoting if we can call [getNextStreamCluster] to get a new StreamCluster. + * + * Initially set to false so that we get a StreamBundle first, because initially + * [streamCluster] is empty. Once [streamBundle] is fetched and [getAdjustedFirstCluster] + * is called, this variable value is same as [streamCluster].hasNext(). + * + * For more explanation on how [streamBundle] and [streamCluster] work, look at the + * documentation in [getNextDataSet]. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + private var hasNextStreamCluster = false + /*fun getPlayStoreAppsOnScroll(browseUrl: String, authData: AuthData) { viewModelScope.launch { *//* @@ -75,53 +96,7 @@ class ApplicationListViewModel @Inject constructor( appListLiveData.value?.first?.let { addAll(it) } } - *//** - * There are four types of urls we are dealing with here. - * - "browseUrl": looks like: homeV2?cat=SOCIAL&c=3 - * - "category urls" or "clusterBrowseUrl": - * Stored in [playStoreCategoryUrls]. looks like: - * getBrowseStream?ecp=ChWiChIIARIGU09DSUFMKgIIB1ICCAE%3D - * getBrowseStream?ecp=CjOiCjAIARIGU09DSUFMGhwKFnJlY3NfdG9waWNfRjkxMjZNYVJ6S1UQOxgDKgIIB1ICCAI%3D - * - "clusterNextPageUrl": looks like: - * getCluster?enpt=CkCC0_-4AzoKMfqegZ0DKwgIEKGz2kgQuMifuAcQ75So0QkQ6Ijz6gwQzvel8QQQprGBmgUQz938owMQyIeljYQwEAcaFaIKEggBEgZTT0NJQUwqAggHUgIIAQ&n=20 - * - "streamNextPageUrl" - not being used in this method. - * - * StreamBundles are obtained from "browseUrls". - * Each StreamBundle can contain StreamClusters, - * (and point to a following StreamBundle with "streamNextPageUrl" - which is not being used here) - * Each StreamCluster contain - * - apps to display - * - a "clusterBrowseUrl" - * - can point to a following StreamCluster with new app data using "clusterNextPageUrl". - * - * -- 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 -> ... - * - * [playStoreCategoryUrls] contains all clusterBrowseUrl 1,2,3 as well as 4,5 ... - * - * Hence we need to go over both "clusterBrowseUrl" (i.e. [playStoreCategoryUrls]) - * as well as available "clusterNextPageUrl". - * The [FusedAPIRepository.getPlayStoreAppCategoryUrls] returns "clusterNextPageUrl" - * in its result (along with list of apps from a StreamCluster.) - * - * Case 1: Initially [nextClusterUrl] will be empty. In that case get the first "clusterBrowseUrl". - * Case 2: After fetching first cluster from getAppsAndNextClusterUrl(), - * nextClusterUrl will be set to a valid "clusterNextPageUrl", - * then this block will not run. - * Case 3: If at any point, the return from getAppsAndNextClusterUrl() below does not - * return non-blank "clusterNextPageUrl", then take the next "clusterBrowseUrl" - * from playStoreCategoryUrls. - * Case 4: All the above cases do not run. This means all available data has been fetched. - * - * [nextClusterUrl] can thus take value of "clusterBrowseUrl" as well as "clusterNextPageUrl" - *//* + *//* if (nextClusterUrl.isBlank()) { nextClusterUrl = playStoreCategoryUrls.getOrElse(categoryUrlsPointer++) { String() } } @@ -175,13 +150,30 @@ class ApplicationListViewModel @Inject constructor( } } + /** + * Get the first StreamBundle object from the category browseUrl, or the subsequent + * StreamBundle objects from the "streamNextPageUrl" of current [streamBundle]. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + * + * @see getNextDataSet + */ private suspend fun getNextStreamBundle( - browseUrl: String, authData: AuthData, + browseUrl: String, ) { streamBundle = fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle) + hasNextStreamBundle = streamBundle.hasNext() } + /** + * The first StreamCluster inside [streamBundle] may not have a "clusterNextPageUrl". + * This method tries to fix that. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + * + * @see getNextDataSet + */ private suspend fun getAdjustedFirstCluster( authData: AuthData, ) { @@ -190,6 +182,14 @@ class ApplicationListViewModel @Inject constructor( } } + /** + * Get all subsequent StreamCluster of the current [streamBundle]. + * Accumulate the data in [streamCluster]. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + * + * @see getNextDataSet + */ private suspend fun getNextStreamCluster( authData: AuthData, ) { @@ -198,12 +198,96 @@ class ApplicationListViewModel @Inject constructor( } } + /** + * Method to add clusterAppList of [newCluster] to [streamCluster], + * but properly point to next StreamCluster. + * Also updates [hasNextStreamCluster]. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ private fun addNewClusterData(newCluster: StreamCluster) { newCluster.run { streamCluster.clusterAppList.addAll(this.clusterAppList) streamCluster.clusterNextPageUrl = this.clusterNextPageUrl streamCluster.clusterBrowseUrl = this.clusterBrowseUrl } + hasNextStreamCluster = newCluster.hasNext() + } + + /** + * This is how the logic works: + * + * StreamBundles are obtained from "browseUrls". + * Each StreamBundle can contain + * - some StreamClusters, + * - point to a following StreamBundle with "streamNextPageUrl" + * (checked by StreamBundle.hasNext()) + * Each StreamCluster contain + * - apps to display + * - a "clusterBrowseUrl" + * - can point to a following StreamCluster with new app data using "clusterNextPageUrl" + * (checked by StreamCluster.hasNext()) + * + * -- 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 -> ... + * + * + * - "browseUrl": looks like: homeV2?cat=SOCIAL&c=3 + * - "clusterBrowseUrl" (not used here): looks like: + * getBrowseStream?ecp=ChWiChIIARIGU09DSUFMKgIIB1ICCAE%3D + * getBrowseStream?ecp=CjOiCjAIARIGU09DSUFMGhwKFnJlY3NfdG9waWNfRjkxMjZNYVJ6S1UQOxgDKgIIB1ICCAI%3D + * - "clusterNextPageUrl" (not directly used here): looks like: + * getCluster?enpt=CkCC0_-4AzoKMfqegZ0DKwgIEKGz2kgQuMifuAcQ75So0QkQ6Ijz6gwQzvel8QQQprGBmgUQz938owMQyIeljYQwEAcaFaIKEggBEgZTT0NJQUwqAggHUgIIAQ&n=20 + * + * ========== Working logic ========== + * + * 1. [streamCluster] accumulates all data from all subsequent network calls. + * Its "clusterNextPageUrl" does point to the next StreamCluster, but its "clusterAppList" + * contains data of all previous network calls. + * + * 2. [streamBundle] is the same value received from [getNextStreamBundle]. + * + * 3. Initially [hasNextStreamCluster] is false, denoting [streamCluster] is empty. + * Initially [hasNextStreamBundle] is true, thus [getNextStreamCluster] is called, fetching the + * first StreamBundle and storing the data in [streamBundle], and getting the first + * StreamCluster data using [getAdjustedFirstCluster]. + * + * 4. From now onwards, + * - [hasNextStreamBundle] is as good as [streamBundle].hasNext() + * - [hasNextStreamCluster] is as good as [streamCluster].hasNext() + * + * 5. When this method is again called when list reaches the end while scrolling on the UI, + * if [hasNextStreamCluster] is true, we will get the next StreamCluster under the current + * StreamBundle object. Once the last StreamCluster is reached, [hasNextStreamCluster] is + * false, we check [hasNextStreamBundle]. If that is true, we load the next StreamBundle. + * This also fetches the first StreamCluster of this bundle, thus re-initialising both + * [hasNextStreamCluster] and [hasNextStreamBundle]. + * + * 6. Once we reach the end of all StreamBundles and all StreamClusters, now calling + * this method makes no network calls. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + private suspend fun getNextDataSet( + authData: AuthData, + browseUrl: String, + ): List { + if (hasNextStreamCluster) { + getNextStreamCluster(authData) + } else if (hasNextStreamBundle) { + getNextStreamBundle(authData, browseUrl) + getAdjustedFirstCluster(authData) + } + return streamCluster.clusterAppList.map { + fusedAPIRepository.mapToFusedApp(it) + } } private fun getOrigin(source: String) = -- GitLab From ca0e16b9e897887ef4f1ebdacfe6a4b452f878d7 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 04:23:45 +0530 Subject: [PATCH 09/35] issue_313 [WIP]: commit ResultSupreme.kt with documents. --- .../foundation/e/apps/api/ResultSupreme.kt | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/api/ResultSupreme.kt diff --git a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt new file mode 100644 index 000000000..02ece3ebd --- /dev/null +++ b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2022 ECORP + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.api + +import foundation.e.apps.utils.enums.ResultStatus +import java.util.concurrent.TimeoutException + +/** + * Another implementation of Result class. + * This removes the use of [ResultStatus] class for different status. + * This class also follows the standard code patterns. However, we still have the same + * flaw that [data] is nullable. As such we may have to add extra null checks or just + * brute force with !! + * + * Also since for each case we now use an inner class with slightly different name, + * we need some refactoring. + * + * Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/313 + */ +sealed class ResultSupreme { + + /** + * Success case. + * Use [isSuccess] to check. + * + * @param data End result of processing. + */ + class Success(data: T): ResultSupreme() { + init { this.data = data } + } + + /** + * Timed out during network related job. + * Use [isTimeout] to check. + * + * @param data The process is expected to output some blank data, but it cannot be null. + * Example can be an empty list. + * @param exception Optional exception from try-catch block. + */ + class Timeout(data: T, exception: Exception = TimeoutException()) : + ResultSupreme() { + init { + this.data = data + this.exception = exception + } + } + + /** + * Miscellaneous error case. + * No valid data from processing. + * Use [isUnknownError] to check. + */ + class Error() : ResultSupreme() { + /** + * @param message A String message to log or display to the user. + * @param exception Optional exception from try-catch block. + */ + constructor(message: String, exception: Exception = Exception()) : this() { + this.message = message + this.exception = exception + } + + /** + * @param data Non-null data. Example a String which could not be parsed into a JSON. + * @param message A optional String message to log or display to the user. + */ + constructor(data: T, message: String = "") : this() { + this.data = data + this.message = message + } + } + + /** + * Data from processing. May be null. + */ + var data: T? = null + + /** + * A custom string message for logging or displaying to the user. + */ + var message: String = "" + + /** + * Exception from try-catch block for error cases. + */ + var exception: Exception = Exception() + + fun isValidData() = data != null + + fun isSuccess() = this is Success && isValidData() + fun isTimeout() = this is Timeout + fun isUnknownError() = this is Error +} \ No newline at end of file -- GitLab From 2a674d46bfdacff81b83238e6c0fd99919836da2 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 15:28:00 +0530 Subject: [PATCH 10/35] issue_313 [WIP]: ResultSupreme create() function that uses ResultStatus to create instance. --- .../foundation/e/apps/api/ResultSupreme.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt index 02ece3ebd..15f2c7c89 100644 --- a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt +++ b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt @@ -105,4 +105,33 @@ sealed class ResultSupreme { fun isSuccess() = this is Success && isValidData() fun isTimeout() = this is Timeout fun isUnknownError() = this is Error + + companion object { + + /** + * Function to create an instance of ResultSupreme from a [ResultStatus] status, + * and other available info - [data], [message], [exception]. + */ + fun create( + status: ResultStatus, + data: T? = null, + message: String = "", + exception: Exception = Exception(), + ): ResultSupreme { + val resultObject = when { + status == ResultStatus.OK && data!= null -> Success(data) + status == ResultStatus.TIMEOUT && data!= null -> Timeout(data) + else -> Error(message, exception) + } + resultObject.apply { + if (isUnknownError()) { + this.data = data + } else { + this.message = message + this.exception = exception + } + } + return resultObject + } + } } \ No newline at end of file -- GitLab From cec43d87a34c90eb335ac9c0e1c61e8e8b552dd1 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 16:37:49 +0530 Subject: [PATCH 11/35] issue_313 [WIP]: ResultSupreme setData() to set non null data. --- .../main/java/foundation/e/apps/api/ResultSupreme.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt index 15f2c7c89..c46dfe8a9 100644 --- a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt +++ b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt @@ -41,7 +41,7 @@ sealed class ResultSupreme { * @param data End result of processing. */ class Success(data: T): ResultSupreme() { - init { this.data = data } + init { setData(data) } } /** @@ -55,7 +55,7 @@ sealed class ResultSupreme { class Timeout(data: T, exception: Exception = TimeoutException()) : ResultSupreme() { init { - this.data = data + setData(data) this.exception = exception } } @@ -80,7 +80,7 @@ sealed class ResultSupreme { * @param message A optional String message to log or display to the user. */ constructor(data: T, message: String = "") : this() { - this.data = data + setData(data) this.message = message } } @@ -89,6 +89,7 @@ sealed class ResultSupreme { * Data from processing. May be null. */ var data: T? = null + private set /** * A custom string message for logging or displaying to the user. @@ -106,6 +107,10 @@ sealed class ResultSupreme { fun isTimeout() = this is Timeout fun isUnknownError() = this is Error + fun setData(data: T) { + this.data = data + } + companion object { /** -- GitLab From a840c1f782a35e6561358dbe92df13379c6f6632 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 16:47:32 +0530 Subject: [PATCH 12/35] issue_5131_2 [WIP]: use ResultSupreme to get category application list data --- .../e/apps/api/fused/FusedAPIImpl.kt | 17 +++++++++-------- .../e/apps/api/fused/FusedAPIRepository.kt | 3 ++- .../ApplicationListFragment.kt | 19 ++++++++++--------- .../ApplicationListViewModel.kt | 14 ++++++++------ 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 9a93b9390..5dbef464e 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -32,6 +32,7 @@ import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.TopChartsHelper import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R +import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.cleanapk.CleanAPKInterface import foundation.e.apps.api.cleanapk.CleanAPKRepository import foundation.e.apps.api.cleanapk.data.categories.Categories @@ -298,8 +299,8 @@ class FusedAPIImpl @Inject constructor( } }*/ - suspend fun getPWAApps(category: String): Pair, ResultStatus> { - var list = mutableListOf() + suspend fun getPWAApps(category: String): ResultSupreme> { + val list = mutableListOf() val status = runCodeBlockWithTimeout({ val response = getPWAAppsResponse(category) response?.apps?.forEach { @@ -308,10 +309,10 @@ class FusedAPIImpl @Inject constructor( list.add(it) } }) - return Pair(list, status) + return ResultSupreme.create(status, list) } - suspend fun getOpenSourceApps(category: String): Pair, ResultStatus> { + suspend fun getOpenSourceApps(category: String): ResultSupreme> { val list = mutableListOf() val status = runCodeBlockWithTimeout({ val response = getOpenSourceAppsResponse(category) @@ -321,7 +322,7 @@ class FusedAPIImpl @Inject constructor( list.add(it) } }) - return Pair(list, status) + return ResultSupreme.create(status, list) } suspend fun getNextStreamBundle( @@ -346,14 +347,14 @@ class FusedAPIImpl @Inject constructor( return gPlayAPIRepository.getNextStreamCluster(authData, currentStreamCluster) } - suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): Pair, ResultStatus> { - var list = mutableListOf() + suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): ResultSupreme> { + val list = mutableListOf() val status = runCodeBlockWithTimeout({ list.addAll(gPlayAPIRepository.listApps(browseUrl, authData).map { app -> app.transformToFusedApp() }) }) - return Pair(list, status) + return ResultSupreme.create(status, list) } suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List { diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 49076de8c..481f36c6c 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -24,6 +24,7 @@ import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.App +import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome @@ -147,7 +148,7 @@ class FusedAPIRepository @Inject constructor( browseUrl: String, authData: AuthData, source: String - ): Pair, ResultStatus> { + ): ResultSupreme> { return when (source) { "Open Source" -> fusedAPIImpl.getOpenSourceApps(category) "PWA" -> fusedAPIImpl.getPWAApps(category) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 6281e2be9..73da9c4b3 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -90,7 +90,7 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li private fun observeDownloadList() { mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list -> - val appList = viewModel.appListLiveData.value?.first?.toMutableList() ?: emptyList() + val appList = viewModel.appListLiveData.value?.data?.toMutableList() ?: emptyList() appList.let { mainActivityViewModel.updateStatusOfFusedApps(it, list) } @@ -99,7 +99,7 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li * Done in one line, so that on Ctrl+click on appListLiveData, * we can see that it is being updated here. */ - viewModel.appListLiveData.apply { value = Pair(appList, value?.second) } + viewModel.appListLiveData.apply { value?.setData(appList) } } } @@ -151,15 +151,16 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li } viewModel.appListLiveData.observe(viewLifecycleOwner) { - listAdapter?.setData(it.first) - if (!isDownloadObserverAdded) { - observeDownloadList() - isDownloadObserverAdded = true - } - stopLoadingUI() - if (it.second != ResultStatus.OK) { + if (!it.isSuccess()) { onTimeout() + } else { + listAdapter?.setData(it.data!!) + if (!isDownloadObserverAdded) { + observeDownloadList() + isDownloadObserverAdded = true + } } + stopLoadingUI() } /* diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 906d4d02c..bf6e432dd 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.utils.enums.Origin @@ -38,7 +39,7 @@ class ApplicationListViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository ) : ViewModel() { - val appListLiveData: MutableLiveData, ResultStatus?>> = MutableLiveData() + val appListLiveData: MutableLiveData>> = MutableLiveData() private var streamBundle = StreamBundle() private var streamCluster = StreamCluster() @@ -113,7 +114,7 @@ class ApplicationListViewModel @Inject constructor( }*/ fun getList(category: String, browseUrl: String, authData: AuthData, source: String) { - if (appListLiveData.value?.first?.isNotEmpty() == true) { + if (appListLiveData.value?.data?.isNotEmpty() == true) { return } viewModelScope.launch(Dispatchers.IO) { @@ -124,8 +125,8 @@ class ApplicationListViewModel @Inject constructor( source ) - if (appsListData.second != ResultStatus.OK) { - appListLiveData.postValue(Pair(listOf(), appsListData.second)) + if (!appsListData.isSuccess()) { + appListLiveData.postValue(appsListData) return@launch } @@ -134,11 +135,12 @@ class ApplicationListViewModel @Inject constructor( * Optimization: packageNames were not used anywhere else, * hence moved here. */ - val packageNames = appsListData.first.map { it.package_name } - fusedAPIRepository.getApplicationDetails( + val packageNames = appsListData.data!!.map { it.package_name } + val pair = fusedAPIRepository.getApplicationDetails( packageNames, authData, getOrigin(source) ) + ResultSupreme.create(pair.second, pair.first) } else { /* * Optimization: Old code was same as the one called above. -- GitLab From 4e1891e125cda4da6976713a04ee8cdeabd2e29f Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 20:31:29 +0530 Subject: [PATCH 13/35] issue_5131_2 [WIP]: use ResultSupreme for methods getNextStreamBundle(), getAdjustedFirstCluster(), getNextStreamCluster() --- .../e/apps/api/fused/FusedAPIImpl.kt | 24 ++++++++++++++----- .../e/apps/api/fused/FusedAPIRepository.kt | 6 ++--- .../ApplicationListViewModel.kt | 20 +++++++++------- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 5dbef464e..b8687c384 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -329,22 +329,34 @@ class FusedAPIImpl @Inject constructor( authData: AuthData, homeUrl: String, currentStreamBundle: StreamBundle, - ): StreamBundle { - return gPlayAPIRepository.getNextStreamBundle(authData, homeUrl, currentStreamBundle) + ): ResultSupreme { + var streamBundle = StreamBundle() + val status = runCodeBlockWithTimeout({ + streamBundle = gPlayAPIRepository.getNextStreamBundle(authData, homeUrl, currentStreamBundle) + }) + return ResultSupreme.create(status, streamBundle) } suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, - ): StreamCluster { - return gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle) + ): ResultSupreme { + var streamCluster = StreamCluster() + val status = runCodeBlockWithTimeout({ + streamCluster = gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle) + }) + return ResultSupreme.create(status, streamCluster) } suspend fun getNextStreamCluster( authData: AuthData, currentStreamCluster: StreamCluster, - ): StreamCluster { - return gPlayAPIRepository.getNextStreamCluster(authData, currentStreamCluster) + ): ResultSupreme { + var streamCluster = StreamCluster() + val status = runCodeBlockWithTimeout({ + streamCluster = gPlayAPIRepository.getNextStreamCluster(authData, currentStreamCluster) + }) + return ResultSupreme.create(status, streamCluster) } suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): ResultSupreme> { diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 481f36c6c..466617028 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -121,21 +121,21 @@ class FusedAPIRepository @Inject constructor( authData: AuthData, homeUrl: String, currentStreamBundle: StreamBundle, - ): StreamBundle { + ): ResultSupreme { return fusedAPIImpl.getNextStreamBundle(authData, homeUrl, currentStreamBundle) } suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, - ): StreamCluster { + ): ResultSupreme { return fusedAPIImpl.getAdjustedFirstCluster(authData, streamBundle) } suspend fun getNextStreamCluster( authData: AuthData, currentStreamCluster: StreamCluster, - ): StreamCluster { + ): ResultSupreme { return fusedAPIImpl.getNextStreamCluster(authData, currentStreamCluster) } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index bf6e432dd..d11280797 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -163,9 +163,11 @@ class ApplicationListViewModel @Inject constructor( private suspend fun getNextStreamBundle( authData: AuthData, browseUrl: String, - ) { - streamBundle = fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle) - hasNextStreamBundle = streamBundle.hasNext() + ): ResultSupreme { + return fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle).apply { + if (isValidData()) streamBundle = data!! + hasNextStreamBundle = streamBundle.hasNext() + } } /** @@ -178,9 +180,9 @@ class ApplicationListViewModel @Inject constructor( */ private suspend fun getAdjustedFirstCluster( authData: AuthData, - ) { - fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle).run { - addNewClusterData(this) + ): ResultSupreme { + return fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle).apply { + if (isValidData()) addNewClusterData(this.data!!) } } @@ -194,9 +196,9 @@ class ApplicationListViewModel @Inject constructor( */ private suspend fun getNextStreamCluster( authData: AuthData, - ) { - fusedAPIRepository.getNextStreamCluster(authData, streamCluster).run { - addNewClusterData(this) + ): ResultSupreme { + return fusedAPIRepository.getNextStreamCluster(authData, streamCluster).apply { + if (isValidData()) addNewClusterData(this.data!!) } } -- GitLab From 85e40f1d1c6728d52049d4cccaec1591654362de Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Tue, 7 Jun 2022 20:41:51 +0530 Subject: [PATCH 14/35] issue_5131_2 [WIP]: remove old code for getting stream cluster urls and loading on scroll --- .../e/apps/api/fused/FusedAPIImpl.kt | 19 ------ .../e/apps/api/fused/FusedAPIRepository.kt | 8 --- .../e/apps/api/gplay/GPlayAPIImpl.kt | 65 ------------------- .../e/apps/api/gplay/GPlayAPIRepository.kt | 8 --- .../ApplicationListViewModel.kt | 41 ------------ 5 files changed, 141 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index b8687c384..9bc581e11 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -369,25 +369,6 @@ class FusedAPIImpl @Inject constructor( return ResultSupreme.create(status, list) } - suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List { - return gPlayAPIRepository.listAppCategoryUrls(browseUrl, authData) - } - - suspend fun getAppsAndNextClusterUrl( - browseUrl: String, - authData: AuthData - ): Triple, String, ResultStatus> { - val appsList = mutableListOf() - var nextUrl = "" - val status = runCodeBlockWithTimeout({ - val gPlayResult = gPlayAPIRepository.getAppsAndNextClusterUrl(browseUrl, authData) - appsList.addAll(gPlayResult.first.map { app -> app.transformToFusedApp() }) - nextUrl = gPlayResult.second - }) - - return Triple(appsList, nextUrl, status) - } - suspend fun getApplicationDetails( packageNameList: List, authData: AuthData, diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 466617028..80a6712b8 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -109,14 +109,6 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.listApps(category, browseUrl, authData) }*/ - suspend fun getPlayStoreAppCategoryUrls(browseUrl: String, authData: AuthData): List { - return fusedAPIImpl.getPlayStoreAppCategoryUrls(browseUrl, authData) - } - - suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Triple, String, ResultStatus> { - return fusedAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData) - } - suspend fun getNextStreamBundle( authData: AuthData, homeUrl: String, diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index dc9918dfe..6ca9cb9a3 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -172,71 +172,6 @@ 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 { - val urlList = mutableListOf() - - 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, 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) - } - /* * Get StreamBundle, either from the homeUrl of a category, * or from the current StreamBundle's next url. diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index 19a83771b..346d4d33b 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -106,12 +106,4 @@ class GPlayAPIRepository @Inject constructor( suspend fun listApps(browseUrl: String, authData: AuthData): List { return gPlayAPIImpl.listApps(browseUrl, authData) } - - suspend fun listAppCategoryUrls(browseUrl: String, authData: AuthData): List { - return gPlayAPIImpl.listAppCategoryUrls(browseUrl, authData) - } - - suspend fun getAppsAndNextClusterUrl(browseUrl: String, authData: AuthData): Pair, String> { - return gPlayAPIImpl.getAppsAndNextClusterUrl(browseUrl, authData) - } } diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index d11280797..df1bb91c9 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -72,47 +72,6 @@ class ApplicationListViewModel @Inject constructor( */ private var hasNextStreamCluster = false - /*fun getPlayStoreAppsOnScroll(browseUrl: String, authData: AuthData) { - viewModelScope.launch { - *//* - * Init condition. - * If category urls are empty or browseUrl has changed, get new category urls. - *//* - if (playStoreCategoryUrls.isEmpty() || browseUrl != lastBrowseUrl) { - categoryUrlsPointer = 0 - playStoreCategoryUrls.clear() - playStoreCategoryUrls.addAll( - fusedAPIRepository.getPlayStoreAppCategoryUrls( - browseUrl.apply { lastBrowseUrl = this }, - authData - ) - ) - } - - *//* - * This is the new list that will be set to the adapter. - * Add existing apps now and add additional apps later. - *//* - val newList = mutableListOf().apply { - appListLiveData.value?.first?.let { addAll(it) } - } - - *//* - if (nextClusterUrl.isBlank()) { - nextClusterUrl = playStoreCategoryUrls.getOrElse(categoryUrlsPointer++) { String() } - } - - if (nextClusterUrl.isNotBlank()) { - fusedAPIRepository.getAppsAndNextClusterUrl(nextClusterUrl, authData).run { - val existingPackageNames = newList.map { it.package_name } - newList.addAll(first.filter { it.package_name !in existingPackageNames }) - appListLiveData.postValue(Pair(newList, third)) - nextClusterUrl = second // set the next "clusterNextPageUrl" - } - } - } - }*/ - fun getList(category: String, browseUrl: String, authData: AuthData, source: String) { if (appListLiveData.value?.data?.isNotEmpty() == true) { return -- GitLab From bfe98ed42bb76e2cb9bdedb05ea731d9e03e48a2 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 00:27:53 +0530 Subject: [PATCH 15/35] issue_5131_2 [WIP]: add restriction field in FusedApp --- .../main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 3 ++- .../main/java/foundation/e/apps/api/fused/data/FusedApp.kt | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 9bc581e11..d2cb0239b 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -941,7 +941,8 @@ class FusedAPIImpl @Inject constructor( originalSize = this.size, appSize = Formatter.formatFileSize(context, this.size), isFree = this.isFree, - price = this.price + price = this.price, + restriction = this.restriction, ) app.updateStatus() return app diff --git a/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt b/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt index d48829311..526a7bfa7 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt @@ -18,6 +18,7 @@ package foundation.e.apps.api.fused.data +import com.aurora.gplayapi.Constants.Restriction import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type @@ -64,4 +65,10 @@ data class FusedApp( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5136 */ var permsFromExodus: List = LIST_OF_NULL, + + /* + * Store restriction from App. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + var restriction: Restriction = Restriction.NOT_RESTRICTED, ) -- GitLab From 1b8c43ad736d25206d3350a19293f932adf766df Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 00:58:30 +0530 Subject: [PATCH 16/35] issue_5131_2 [WIP]: create FusedAPIImpl.filterRestrictedGPlayApps() to filter out restricted apps. --- .../e/apps/api/fused/FusedAPIImpl.kt | 33 +++++++++++++++++++ .../e/apps/api/fused/FusedAPIRepository.kt | 7 ++++ 2 files changed, 40 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index d2cb0239b..664c428af 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -469,6 +469,39 @@ class FusedAPIImpl @Inject constructor( return Pair(fusedAppList, status) } + /** + * Filter out apps which are restricted, whose details cannot be fetched. + * If an app is restricted, we do try to fetch the app details inside a + * try-catch block. If that fails, we remove the app, else we keep it even + * if it is restricted. + * + * Popular example: "com.skype.m2" + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + suspend fun filterRestrictedGPlayApps( + authData: AuthData, + appList: List, + ): ResultSupreme> { + val filteredFusedApps = mutableListOf() + val status = runCodeBlockWithTimeout({ + appList.forEach { + if (it.restriction != Constants.Restriction.NOT_RESTRICTED) { + try { + gPlayAPIRepository.getAppDetails(it.packageName, authData)?.let { app -> + filteredFusedApps.add(app.transformToFusedApp()) + } + } catch (e: Exception) {} + } else { + filteredFusedApps.add(it.transformToFusedApp()) + } + } + }) + + return ResultSupreme.create(status, filteredFusedApps) + } + suspend fun getApplicationDetails( id: String, packageName: String, diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 80a6712b8..936fdb215 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -63,6 +63,13 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getApplicationDetails(packageNameList, authData, origin) } + suspend fun filterRestrictedGPlayApps( + authData: AuthData, + appList: List, + ): ResultSupreme> { + return fusedAPIImpl.filterRestrictedGPlayApps(authData, appList) + } + suspend fun getApplicationDetails( id: String, packageName: String, -- GitLab From b8c85ee341e65700435003072f9798fcf72586ab Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 01:20:55 +0530 Subject: [PATCH 17/35] issue_5131_2 [WIP]: implement filterRestrictedGPlayApps in ApplicationListViewModel.kt --- .../e/apps/applicationlist/ApplicationListViewModel.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index df1bb91c9..677f20e81 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -241,16 +241,14 @@ class ApplicationListViewModel @Inject constructor( private suspend fun getNextDataSet( authData: AuthData, browseUrl: String, - ): List { + ): ResultSupreme> { if (hasNextStreamCluster) { getNextStreamCluster(authData) } else if (hasNextStreamBundle) { getNextStreamBundle(authData, browseUrl) getAdjustedFirstCluster(authData) } - return streamCluster.clusterAppList.map { - fusedAPIRepository.mapToFusedApp(it) - } + return fusedAPIRepository.filterRestrictedGPlayApps(authData, streamCluster.clusterAppList) } private fun getOrigin(source: String) = -- GitLab From 5cd63064bc07df8df36a8dba324f516672238adb Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 01:21:58 +0530 Subject: [PATCH 18/35] issue_5131_2 [WIP]: delete mapToFusedApp function. This reverts commit 62ef53fc --- app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt | 2 -- .../java/foundation/e/apps/api/fused/FusedAPIRepository.kt | 4 ---- 2 files changed, 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 664c428af..d6b74af72 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -981,8 +981,6 @@ class FusedAPIImpl @Inject constructor( return app } - fun mapToFusedApp(app: App): FusedApp = app.transformToFusedApp() - /** * Get fused app installation status. * Applicable for both native apps and PWAs. diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 936fdb215..0ad797a39 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -138,10 +138,6 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getNextStreamCluster(authData, currentStreamCluster) } - fun mapToFusedApp(app: App): FusedApp { - return fusedAPIImpl.mapToFusedApp(app) - } - suspend fun getAppsListBasedOnCategory( category: String, browseUrl: String, -- GitLab From 58dbe3b06c0b8546d804f9bc9f274cd2915a7374 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 01:51:22 +0530 Subject: [PATCH 19/35] issue_313 [WIP]: create ResultSupreme.replicate method --- .../foundation/e/apps/api/ResultSupreme.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt index c46dfe8a9..444c61228 100644 --- a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt +++ b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt @@ -138,5 +138,31 @@ sealed class ResultSupreme { } return resultObject } + + /** + * Create a similar [ResultSupreme] instance i.e. of type [Success], [Timeout]... + * using a supplied [result] object but with a different generic type and new data. + * + * @param result Class of [ResultSupreme] whose replica is to be made. + * @param newData Nullable new data for this replica. + * @param message Optional new message for this replica. If not provided, + * the new object will get the message from [result]. + * @param exception Optional new exception for this replica. If not provided, + * the new object will get the exception from [result]. + */ + fun replicate( + result: ResultSupreme<*>, + newData: T?, + message: String? = null, + exception: Exception? = null, + ): ResultSupreme { + val status = when (result) { + is Success -> ResultStatus.OK + is Timeout -> ResultStatus.TIMEOUT + is Error -> ResultStatus.UNKNOWN + } + return create(status, newData, message ?: result.message, + exception ?: result.exception) + } } } \ No newline at end of file -- GitLab From bca9a8fa7fae0a95b3d1120684ab95c8bdf3e4eb Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 01:56:15 +0530 Subject: [PATCH 20/35] issue_5131_2 [WIP]: return immediately if getNextStreamCluster(), getNextStreamBundle() or getAdjustedFirstCluster() fails inside getNextDataSet() --- .../ApplicationListViewModel.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 677f20e81..c499cc4fa 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -29,7 +29,6 @@ import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.utils.enums.Origin -import foundation.e.apps.utils.enums.ResultStatus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -243,10 +242,22 @@ class ApplicationListViewModel @Inject constructor( browseUrl: String, ): ResultSupreme> { if (hasNextStreamCluster) { - getNextStreamCluster(authData) + getNextStreamCluster(authData).run { + if (!isSuccess()) { + return ResultSupreme.replicate(this, listOf()) + } + } } else if (hasNextStreamBundle) { - getNextStreamBundle(authData, browseUrl) - getAdjustedFirstCluster(authData) + getNextStreamBundle(authData, browseUrl).run { + if (!isSuccess()) { + return ResultSupreme.replicate(this, listOf()) + } + getAdjustedFirstCluster(authData).run { + if (!isSuccess()) { + return ResultSupreme.replicate(this, listOf()) + } + } + } } return fusedAPIRepository.filterRestrictedGPlayApps(authData, streamCluster.clusterAppList) } -- GitLab From 6e69db0439a88aedffb7030041ab2d00245336b8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 04:28:35 +0530 Subject: [PATCH 21/35] issue_5131_2 [WIP]: fix initial cluster not being properly fetched. --- app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index 6ca9cb9a3..bc874a837 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -210,7 +210,7 @@ class GPlayAPIImpl @Inject constructor( ): StreamCluster { return withContext(Dispatchers.IO) { if (streamBundle.streamClusters.isNotEmpty()) { - streamBundle.streamClusters[0]?.run { + streamBundle.streamClusters.values.toList()[0].run { if (hasNext()) { /* * If zeroth StreamCluster's next url is not blank, return it. -- GitLab From 11f8e1ef265ce52c70723642e578bbbecadd164a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 05:12:04 +0530 Subject: [PATCH 22/35] issue_5131_2 [WIP]: add a pointer variable to GPlayAPIImpl.getAdjustedFirstCluster(), update documentation. --- .../e/apps/api/fused/FusedAPIImpl.kt | 3 ++- .../e/apps/api/fused/FusedAPIRepository.kt | 3 ++- .../e/apps/api/gplay/GPlayAPIImpl.kt | 23 +++++++++++++++---- .../e/apps/api/gplay/GPlayAPIRepository.kt | 3 ++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index d6b74af72..5e383976f 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -340,10 +340,11 @@ class FusedAPIImpl @Inject constructor( suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, + pointer: Int = 0, ): ResultSupreme { var streamCluster = StreamCluster() val status = runCodeBlockWithTimeout({ - streamCluster = gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle) + streamCluster = gPlayAPIRepository.getAdjustedFirstCluster(authData, streamBundle, pointer) }) return ResultSupreme.create(status, streamCluster) } diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 0ad797a39..1bc89fbe9 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -127,8 +127,9 @@ class FusedAPIRepository @Inject constructor( suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, + pointer: Int = 0, ): ResultSupreme { - return fusedAPIImpl.getAdjustedFirstCluster(authData, streamBundle) + return fusedAPIImpl.getAdjustedFirstCluster(authData, streamBundle, pointer) } suspend fun getNextStreamCluster( diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index bc874a837..49020d3f7 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -197,20 +197,35 @@ class GPlayAPIImpl @Inject constructor( } } - /* + /** * Get first StreamCluster of a StreamBundle. - * Ideally it would just be streamBundle.streamClusters[0], but in case the zeroth StreamCluster + * + * Ideally it would just be streamBundle.streamClusters[[pointer]], but in case the StreamCluster * does not have a next url, we need to get a StreamCluster which has a clusterNextPageUrl. * + * This does not always operate on zeroth StreamCluster of [streamBundle]. + * A StreamBundle can have many StreamClusters, each of the individual StreamCluster can point + * to completely different StreamClusters. + * + * StreamBundle 1 (streamNextPageUrl points to StreamBundle 2) + * StreamCluster 1 -> StreamCluster 1.1 -> StreamCluster 1.2 .... + * StreamCluster 2 -> StreamCluster 2.1 -> StreamCluster 2.2 .... + * StreamCluster 3 -> StreamCluster 3.1 -> StreamCluster 3.2 .... + * + * Here [pointer] refers to the position of StreamCluster 1, 2, 3.... but not 1.1, 2.1 .... + * The subsequent clusters (1.1, 1.2, .. 2.1 ..) are accessed by [getNextStreamCluster]. + * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] */ suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, + pointer: Int = 0, ): StreamCluster { return withContext(Dispatchers.IO) { - if (streamBundle.streamClusters.isNotEmpty()) { - streamBundle.streamClusters.values.toList()[0].run { + val clusterSize = streamBundle.streamClusters.size + if (clusterSize != 0 && pointer < clusterSize && pointer >= 0) { + streamBundle.streamClusters.values.toList()[pointer].run { if (hasNext()) { /* * If zeroth StreamCluster's next url is not blank, return it. diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt index 346d4d33b..737f206a1 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt @@ -92,8 +92,9 @@ class GPlayAPIRepository @Inject constructor( suspend fun getAdjustedFirstCluster( authData: AuthData, streamBundle: StreamBundle, + pointer: Int = 0, ): StreamCluster { - return gPlayAPIImpl.getAdjustedFirstCluster(authData, streamBundle) + return gPlayAPIImpl.getAdjustedFirstCluster(authData, streamBundle, pointer) } suspend fun getNextStreamCluster( -- GitLab From eed5b4c7159f1a5e08cbec961fe8ac10201c85ec Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 05:14:00 +0530 Subject: [PATCH 23/35] issue_5131_2 [WIP]: implement sending clusterPointer from ApplicationListViewModel.kt --- .../ApplicationListViewModel.kt | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index c499cc4fa..af8403fd8 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -43,6 +43,8 @@ class ApplicationListViewModel @Inject constructor( private var streamBundle = StreamBundle() private var streamCluster = StreamCluster() + private var clusterPointer = 0 + /** * Variable denoting if we can call [getNextStreamCluster] to get a new StreamBundle. * @@ -113,6 +115,7 @@ class ApplicationListViewModel @Inject constructor( /** * Get the first StreamBundle object from the category browseUrl, or the subsequent * StreamBundle objects from the "streamNextPageUrl" of current [streamBundle]. + * Also resets the [clusterPointer] to 0. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] * @@ -125,6 +128,7 @@ class ApplicationListViewModel @Inject constructor( return fusedAPIRepository.getNextStreamBundle(authData, browseUrl, streamBundle).apply { if (isValidData()) streamBundle = data!! hasNextStreamBundle = streamBundle.hasNext() + clusterPointer = 0 } } @@ -139,7 +143,7 @@ class ApplicationListViewModel @Inject constructor( private suspend fun getAdjustedFirstCluster( authData: AuthData, ): ResultSupreme { - return fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle).apply { + return fusedAPIRepository.getAdjustedFirstCluster(authData, streamBundle, clusterPointer).apply { if (isValidData()) addNewClusterData(this.data!!) } } @@ -193,12 +197,12 @@ class ApplicationListViewModel @Inject constructor( * -- 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 .... + * StreamCluster 1 -> StreamCluster 1.1 -> StreamCluster 1.2 .... + * StreamCluster 2 -> StreamCluster 2.1 -> StreamCluster 2.2 .... + * StreamCluster 3 -> StreamCluster 3.1 -> StreamCluster 3.2 .... * StreamBundle 2 - * clusterBroseUrl 4 -> ... - * clusterBroseUrl 5 -> ... + * StreamCluster 4 -> ... + * StreamCluster 5 -> ... * * * - "browseUrl": looks like: homeV2?cat=SOCIAL&c=3 @@ -212,23 +216,40 @@ class ApplicationListViewModel @Inject constructor( * * 1. [streamCluster] accumulates all data from all subsequent network calls. * Its "clusterNextPageUrl" does point to the next StreamCluster, but its "clusterAppList" - * contains data of all previous network calls. + * contains accumulated data of all previous network calls. * * 2. [streamBundle] is the same value received from [getNextStreamBundle]. * * 3. Initially [hasNextStreamCluster] is false, denoting [streamCluster] is empty. - * Initially [hasNextStreamBundle] is true, thus [getNextStreamCluster] is called, fetching the - * first StreamBundle and storing the data in [streamBundle], and getting the first + * Initially [clusterPointer] = 0, [streamBundle].streamClusters.size = 0, + * hence 2nd case also does not execute. + * However, initially [hasNextStreamBundle] is true, thus [getNextStreamBundle] is called, + * fetching the first StreamBundle and storing the data in [streamBundle], and getting the first * StreamCluster data using [getAdjustedFirstCluster]. * + * NOTE: [getAdjustedFirstCluster] is used to fetch StreamCluster 1, 2, 3 .. in the above + * diagram with help of [clusterPointer]. For subsequent StreamCluster 1.1, 1.2 .. 2.1 .. + * [getNextStreamCluster] is used. + * * 4. From now onwards, * - [hasNextStreamBundle] is as good as [streamBundle].hasNext() * - [hasNextStreamCluster] is as good as [streamCluster].hasNext() * - * 5. When this method is again called when list reaches the end while scrolling on the UI, + * 5.1. When this method is again called when list reaches the end while scrolling on the UI, * if [hasNextStreamCluster] is true, we will get the next StreamCluster under the current * StreamBundle object. Once the last StreamCluster is reached, [hasNextStreamCluster] is - * false, we check [hasNextStreamBundle]. If that is true, we load the next StreamBundle. + * false, we move to the next case. + * + * 5.2. In the step 5.1 we have been traversing along the path StreamCluster 1 -> 1.1 -> 1.2 .. + * Once that path reaches an end, we need to jump to StreamCluster 2 -> 2.1 -> 2.2 .. + * This is achieved by the second condition using [clusterPointer]. We increment the + * pointer and call [getAdjustedFirstCluster] again to start from StreamCluster 2. + * + * 5.3. Once we no longer have any more beginning StreamClusters, i.e + * [clusterPointer] exceeds [streamBundle].streamClusters size, the second condition no + * longer holds. Now we should try to go to a different StreamBundle. + * Using the above diagram, we move to StreamBundle 1 -> 2. + * We check [hasNextStreamBundle]. If that is true, we load the next StreamBundle. * This also fetches the first StreamCluster of this bundle, thus re-initialising both * [hasNextStreamCluster] and [hasNextStreamBundle]. * @@ -247,6 +268,13 @@ class ApplicationListViewModel @Inject constructor( return ResultSupreme.replicate(this, listOf()) } } + } else if (clusterPointer < streamBundle.streamClusters.size) { + ++clusterPointer + getAdjustedFirstCluster(authData).run { + if (!isSuccess()) { + return ResultSupreme.replicate(this, listOf()) + } + } } else if (hasNextStreamBundle) { getNextStreamBundle(authData, browseUrl).run { if (!isSuccess()) { -- GitLab From 8ca12deb113b7a5fd60b30d0984889873394f7b8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 05:15:05 +0530 Subject: [PATCH 24/35] issue_5131_2 [WIP]: make sure of distinct package names in the list --- .../e/apps/applicationlist/ApplicationListViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index af8403fd8..2ba8f0c70 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -173,7 +173,11 @@ class ApplicationListViewModel @Inject constructor( */ private fun addNewClusterData(newCluster: StreamCluster) { newCluster.run { - streamCluster.clusterAppList.addAll(this.clusterAppList) + streamCluster.clusterAppList.apply { + val addedList = this + newCluster.clusterAppList + clear() + addAll(addedList.distinctBy { it.packageName }) + } streamCluster.clusterNextPageUrl = this.clusterNextPageUrl streamCluster.clusterBrowseUrl = this.clusterBrowseUrl } -- GitLab From 800dfefee1bf79f20fa7492ab20b63edcdf6b05c Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 14:46:20 +0530 Subject: [PATCH 25/35] issue_5131_2 [WIP]: implement from UI, loading data from new StreamCluster and StreamBundle --- .../ApplicationListFragment.kt | 10 +++--- .../ApplicationListViewModel.kt | 36 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 73da9c4b3..f4dd29afd 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -209,20 +209,20 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li args.source ) - /*if (args.source != "Open Source" && args.source != "PWA") { - *//* + 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 - *//* + */ binding.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) + viewModel.loadMore(authData, args.browseUrl) } } }) - }*/ + } appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { updateProgressOfDownloadingItems(binding.recyclerView, it) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 2ba8f0c70..3fa5afbe9 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -78,23 +78,29 @@ class ApplicationListViewModel @Inject constructor( return } viewModelScope.launch(Dispatchers.IO) { - val appsListData = fusedAPIRepository.getAppsListBasedOnCategory( - category, - browseUrl, - authData, - source - ) + val appsListData = if (source == "Open Source" || source == "PWA") { + fusedAPIRepository.getAppsListBasedOnCategory( + category, + browseUrl, + authData, + source + ) + } else { + getNextDataSet(authData, browseUrl) + } + + appListLiveData.postValue(appsListData) - if (!appsListData.isSuccess()) { + /*if (!appsListData.isSuccess()) { appListLiveData.postValue(appsListData) return@launch } val applicationDetailsWithStatus = if (!source.contentEquals("PWA")) { - /* + *//* * Optimization: packageNames were not used anywhere else, * hence moved here. - */ + *//* val packageNames = appsListData.data!!.map { it.package_name } val pair = fusedAPIRepository.getApplicationDetails( packageNames, authData, @@ -102,13 +108,19 @@ class ApplicationListViewModel @Inject constructor( ) ResultSupreme.create(pair.second, pair.first) } else { - /* + *//* * Optimization: Old code was same as the one called above. - */ + *//* appsListData } - appListLiveData.postValue(applicationDetailsWithStatus) + appListLiveData.postValue(applicationDetailsWithStatus)*/ + } + } + + fun loadMore(authData: AuthData, browseUrl: String) { + viewModelScope.launch { + appListLiveData.postValue(getNextDataSet(authData, browseUrl)) } } -- GitLab From 5c63d3ec8efccd53b6f5bf49d5bd6dace87e4cdc Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 14:50:57 +0530 Subject: [PATCH 26/35] issue_5131_2: create an "isLoading" flag to prevent additional network calls when already loading some data. --- .../applicationlist/ApplicationListViewModel.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 3fa5afbe9..3e205f59d 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -45,6 +45,8 @@ class ApplicationListViewModel @Inject constructor( private var clusterPointer = 0 + var isLoading = false + /** * Variable denoting if we can call [getNextStreamCluster] to get a new StreamBundle. * @@ -74,7 +76,7 @@ class ApplicationListViewModel @Inject constructor( private var hasNextStreamCluster = false fun getList(category: String, browseUrl: String, authData: AuthData, source: String) { - if (appListLiveData.value?.data?.isNotEmpty() == true) { + if (appListLiveData.value?.data?.isNotEmpty() == true || isLoading) { return } viewModelScope.launch(Dispatchers.IO) { @@ -120,7 +122,9 @@ class ApplicationListViewModel @Inject constructor( fun loadMore(authData: AuthData, browseUrl: String) { viewModelScope.launch { - appListLiveData.postValue(getNextDataSet(authData, browseUrl)) + if (!isLoading) { + appListLiveData.postValue(getNextDataSet(authData, browseUrl)) + } } } @@ -278,6 +282,8 @@ class ApplicationListViewModel @Inject constructor( authData: AuthData, browseUrl: String, ): ResultSupreme> { + isLoading = true + if (hasNextStreamCluster) { getNextStreamCluster(authData).run { if (!isSuccess()) { @@ -304,6 +310,9 @@ class ApplicationListViewModel @Inject constructor( } } return fusedAPIRepository.filterRestrictedGPlayApps(authData, streamCluster.clusterAppList) + .apply { + isLoading = false + } } private fun getOrigin(source: String) = -- GitLab From 06d8b7301cc5eefc891b2d7b20eda7bf5cb8bed8 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 15:53:50 +0530 Subject: [PATCH 27/35] issue_5131_2: avoid a crash in GPlayAPIImpl.validateAuthData() --- .../java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index 49020d3f7..7b0f869f2 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -78,8 +78,13 @@ class GPlayAPIImpl @Inject constructor( suspend fun validateAuthData(authData: AuthData): Boolean { var validity: Boolean withContext(Dispatchers.IO) { - val authValidator = AuthValidator(authData).using(gPlayHttpClient) - validity = authValidator.isValid() + validity = try { + val authValidator = AuthValidator(authData).using(gPlayHttpClient) + authValidator.isValid() + } catch (e: Exception) { + e.printStackTrace() + false + } } return validity } -- GitLab From c0293da2271b58ef25a1d16b743facbf46b24d5b Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 17:24:24 +0530 Subject: [PATCH 28/35] issue_5131_2: create a "canLoadMore" function to return a boolean if more data can be loaded --- .../e/apps/applicationlist/ApplicationListViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 3e205f59d..d4af7bfdc 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -315,6 +315,13 @@ class ApplicationListViewModel @Inject constructor( } } + /** + * Function is used to check if we can load more data. + * It is also used to show a loading progress bar at the end of the list. + */ + fun canLoadMore(): Boolean = + hasNextStreamCluster || clusterPointer < streamBundle.streamClusters.size || hasNextStreamBundle + private fun getOrigin(source: String) = if (source.contentEquals("Open Source")) Origin.CLEANAPK else Origin.GPLAY } -- GitLab From 82eeb39ad4b34b4d2647a7e5ddf242168a07e06e Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 18:21:15 +0530 Subject: [PATCH 29/35] issue_5131_2: Add isPlaceHolder boolean variable in FusedApp.kt --- .../main/java/foundation/e/apps/api/fused/data/FusedApp.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt b/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt index 526a7bfa7..7c99cc0c5 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/data/FusedApp.kt @@ -71,4 +71,11 @@ data class FusedApp( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] */ var restriction: Restriction = Restriction.NOT_RESTRICTED, + + /* + * Show a blank app at the end during loading. + * Used when loading apps of a category. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + var isPlaceHolder: Boolean = false, ) -- GitLab From 8bc6d8ba620f1565bc2972869d8648b1a3bcddd3 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Wed, 8 Jun 2022 18:26:39 +0530 Subject: [PATCH 30/35] issue_5131_2: handle UI for placeholder FusedApp in ApplicationListRVAdapter. --- .../model/ApplicationListRVAdapter.kt | 20 +++++++++++++++++++ .../main/res/layout/application_list_item.xml | 11 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt index f610b5c31..1bc9652ed 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt @@ -25,6 +25,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.core.content.ContextCompat +import androidx.core.view.children import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -99,6 +100,25 @@ class ApplicationListRVAdapter( val searchApp = getItem(position) val shimmerDrawable = ShimmerDrawable().apply { setShimmer(shimmer) } + /* + * A placeholder entry is one where we only show a loading progress bar, + * instead of an app entry. + * It is usually done to signify more apps are being loaded at the end of the list. + * + * We hide all view elements other than the circular progress bar. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + */ + if (searchApp.isPlaceHolder) { + val progressBar = holder.binding.placeholderProgressBar + holder.binding.root.children.forEach { + it.visibility = if (it != progressBar) View.INVISIBLE + else View.VISIBLE + } + // Do not process anything else for this entry + return + } + holder.binding.apply { if (searchApp.privacyScore == -1) { hidePrivacyScore() diff --git a/app/src/main/res/layout/application_list_item.xml b/app/src/main/res/layout/application_list_item.xml index 921a9273f..31312cde5 100644 --- a/app/src/main/res/layout/application_list_item.xml +++ b/app/src/main/res/layout/application_list_item.xml @@ -29,6 +29,17 @@ android:clickable="true" android:focusable="true"> + + Date: Wed, 8 Jun 2022 18:40:00 +0530 Subject: [PATCH 31/35] issue_5131_2: create and use method addPlaceHolderAppIfNeeded() in ApplicationListViewModel --- .../ApplicationListViewModel.kt | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index d4af7bfdc..c1fe727a0 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -88,7 +88,9 @@ class ApplicationListViewModel @Inject constructor( source ) } else { - getNextDataSet(authData, browseUrl) + getNextDataSet(authData, browseUrl).apply { + addPlaceHolderAppIfNeeded(this) + } } appListLiveData.postValue(appsListData) @@ -120,10 +122,48 @@ class ApplicationListViewModel @Inject constructor( } } + /** + * Add a placeholder app at the end if more data can be loaded. + * "Placeholder" app shows a simple progress bar in the RecyclerView, indicating that + * more apps are being loaded. + * + * Note that it mutates the [ResultSupreme] object passed to it. + * + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5131 [2] + * + * @param result object from [getNextDataSet]. Data of this object will be updated + * if [canLoadMore] is true. + * + * @return true if a placeholder app was added, false otherwise. + */ + private fun addPlaceHolderAppIfNeeded(result: ResultSupreme>): Boolean { + result.apply { + if (isSuccess() && canLoadMore()) { + // Add an empty app at the end if more data can be loaded on scroll + val newData = data!!.toMutableList() + newData.add(FusedApp(isPlaceHolder = true)) + setData(newData) + return true + } + } + return false + } + fun loadMore(authData: AuthData, browseUrl: String) { viewModelScope.launch { if (!isLoading) { - appListLiveData.postValue(getNextDataSet(authData, browseUrl)) + val result = getNextDataSet(authData, browseUrl) + appListLiveData.postValue(result) + /* + * Check if a placeholder app is to be added at the end. + * If yes then post the updated result. + * We post this separately as it helps clear any previous placeholder app + * and ensures only a single placeholder app is present at the end of the + * list, and none at the middle of the list. + */ + if (addPlaceHolderAppIfNeeded(result)) { + appListLiveData.postValue(result) + } } } } -- GitLab From 8cea7e141e75a5379ed598151f3cd3573d32f540 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 9 Jun 2022 05:15:20 +0530 Subject: [PATCH 32/35] issue_5131_2: loadMore data automatically if new filtered data size is same as old data size. --- .../applicationlist/ApplicationListViewModel.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index c1fe727a0..2ae4cb1ea 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -152,7 +152,9 @@ class ApplicationListViewModel @Inject constructor( fun loadMore(authData: AuthData, browseUrl: String) { viewModelScope.launch { if (!isLoading) { + val lastCount: Int = streamCluster.clusterAppList.size val result = getNextDataSet(authData, browseUrl) + val newCount = streamCluster.clusterAppList.size appListLiveData.postValue(result) /* * Check if a placeholder app is to be added at the end. @@ -164,6 +166,17 @@ class ApplicationListViewModel @Inject constructor( if (addPlaceHolderAppIfNeeded(result)) { appListLiveData.postValue(result) } + + /* + * Old count and new count can be same if new StreamCluster has apps which + * are already shown, i.e. with duplicate package names. + * In that case, if we can load more data, we do it from here itself, + * because recyclerview scroll listener will not trigger itself twice + * for the same data. + */ + if (result.isSuccess() && lastCount == newCount && canLoadMore()) { + loadMore(authData, browseUrl) + } } } } -- GitLab From 67ed34abbddcc85071329e123d7266478e75de85 Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 9 Jun 2022 05:16:57 +0530 Subject: [PATCH 33/35] issue_5131_2: setup callback when placeHolder is shown and loadMore apps when it is called. --- .../applicationlist/ApplicationListFragment.kt | 14 ++++++++++++++ .../model/ApplicationListRVAdapter.kt | 3 +++ 2 files changed, 17 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index f4dd29afd..31ace4667 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -222,6 +222,20 @@ class ApplicationListFragment : TimeoutFragment(R.layout.fragment_application_li } } }) + /* + * This listener comes handy in the case where only 2-3 apps are loaded + * in the first cluster. + * In that case, unless the user scrolls, the above listener will not be + * triggered. Setting this onPlaceHolderShow() callback loads new data + * automatically if the initial data is less. + */ + binding.recyclerView.adapter.apply { + if (this is ApplicationListRVAdapter) { + onPlaceHolderShow = { + viewModel.loadMore(authData, args.browseUrl) + } + } + } } appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { diff --git a/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt index 1bc9652ed..c6ed486a5 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/model/ApplicationListRVAdapter.kt @@ -80,6 +80,8 @@ class ApplicationListRVAdapter( .setAutoStart(true) .build() + var onPlaceHolderShow: (() -> Unit)? = null + inner class ViewHolder(val binding: ApplicationListItemBinding) : RecyclerView.ViewHolder(binding.root) { var isPurchasedLiveData: LiveData = MutableLiveData() @@ -115,6 +117,7 @@ class ApplicationListRVAdapter( it.visibility = if (it != progressBar) View.INVISIBLE else View.VISIBLE } + onPlaceHolderShow?.invoke() // Do not process anything else for this entry return } -- GitLab From fce1c1824ea397ebdbcedaf0a3facfc8a29d174a Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 9 Jun 2022 16:13:21 +0530 Subject: [PATCH 34/35] issue_5131_2: ktlint fixes --- .../main/java/foundation/e/apps/MainActivity.kt | 9 ++++++--- .../foundation/e/apps/MainActivityViewModel.kt | 2 -- .../main/java/foundation/e/apps/api/JobResult.kt | 14 +++++++------- .../java/foundation/e/apps/api/ResultSupreme.kt | 16 +++++++++------- .../foundation/e/apps/api/fused/FusedAPIImpl.kt | 13 +++++++------ .../e/apps/api/fused/FusedAPIRepository.kt | 4 ++-- .../foundation/e/apps/api/gplay/GPlayAPIImpl.kt | 6 ++---- .../e/apps/application/ApplicationFragment.kt | 2 +- .../applicationlist/ApplicationListFragment.kt | 3 +-- .../java/foundation/e/apps/home/HomeFragment.kt | 3 +-- .../manager/download/data/DownloadProgressLD.kt | 1 - .../foundation/e/apps/search/SearchFragment.kt | 3 +-- .../foundation/e/apps/updates/UpdatesFragment.kt | 2 +- .../e/apps/utils/enums/ResultStatus.kt | 2 +- .../apps/utils/parentFragment/TimeoutFragment.kt | 10 +++++----- 15 files changed, 44 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 733b68107..f095a39d5 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -45,8 +45,8 @@ import foundation.e.apps.setup.signin.SignInViewModel import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import java.io.File import java.util.UUID @@ -143,8 +143,11 @@ class MainActivity : AppCompatActivity() { Log.d(TAG, "Timeout validating auth data!") val lastFragment = navHostFragment.childFragmentManager.fragments[0] if (lastFragment is TimeoutFragment) { - Log.d(TAG, "Displaying timeout from MainActivity on fragment: " - + lastFragment::class.java.name) + Log.d( + TAG, + "Displaying timeout from MainActivity on fragment: " + + lastFragment::class.java.name + ) lastFragment.onTimeout() } } diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt index 23083096b..ec8d4123c 100644 --- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt @@ -18,7 +18,6 @@ package foundation.e.apps -import android.app.Activity import android.content.Context import android.graphics.Bitmap import android.os.Build @@ -41,7 +40,6 @@ import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.cleanapk.blockedApps.BlockedAppRepository import foundation.e.apps.api.ecloud.EcloudRepository -import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload diff --git a/app/src/main/java/foundation/e/apps/api/JobResult.kt b/app/src/main/java/foundation/e/apps/api/JobResult.kt index 9a57011d2..b632a1366 100644 --- a/app/src/main/java/foundation/e/apps/api/JobResult.kt +++ b/app/src/main/java/foundation/e/apps/api/JobResult.kt @@ -16,9 +16,9 @@ open class JobResult private constructor(val status: ResultStatus) { * If needed to just pass a single data element with status for API requests, * see the static methods success(), error(), loading() (in companion object). */ - class of1 (val data1: A, status: ResultStatus): JobResult(status) - class of2 (val data1: A, val data2: B, status: ResultStatus): JobResult(status) - class of3 (val data1: A, val data2: B, val data3: C, status: ResultStatus): JobResult(status) + class of1 (val data1: A, status: ResultStatus) : JobResult(status) + class of2 (val data1: A, val data2: B, status: ResultStatus) : JobResult(status) + class of3 (val data1: A, val data2: B, val data3: C, status: ResultStatus) : JobResult(status) var message = "" @@ -29,7 +29,7 @@ open class JobResult private constructor(val status: ResultStatus) { * For non-null return type, directly use of1, of2, of3 ... classes * and directly access data1, data2, data3 ... */ - val data: T? get() = when(this) { + val data: T? get() = when (this) { is of1 -> this.data1 is of2 -> this.data1 is of3 -> this.data1 @@ -46,12 +46,12 @@ open class JobResult private constructor(val status: ResultStatus) { message?.let { this.message = message } } } - fun create(data1: A, data2: B, status: ResultStatus, message: String? = null): of2 { + fun create(data1: A, data2: B, status: ResultStatus, message: String? = null): of2 { return of2(data1, data2, status).apply { message?.let { this.message = message } } } - fun create(data1: A, data2: B, data3: C, status: ResultStatus, message: String? = null): of3 { + fun create(data1: A, data2: B, data3: C, status: ResultStatus, message: String? = null): of3 { return of3(data1, data2, data3, status).apply { message?.let { this.message = message } } @@ -75,4 +75,4 @@ open class JobResult private constructor(val status: ResultStatus) { else JobResult.of1(data, ResultStatus.LOADING) }*/ } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt index 444c61228..152803513 100644 --- a/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt +++ b/app/src/main/java/foundation/e/apps/api/ResultSupreme.kt @@ -40,7 +40,7 @@ sealed class ResultSupreme { * * @param data End result of processing. */ - class Success(data: T): ResultSupreme() { + class Success(data: T) : ResultSupreme() { init { setData(data) } } @@ -89,7 +89,7 @@ sealed class ResultSupreme { * Data from processing. May be null. */ var data: T? = null - private set + private set /** * A custom string message for logging or displaying to the user. @@ -124,8 +124,8 @@ sealed class ResultSupreme { exception: Exception = Exception(), ): ResultSupreme { val resultObject = when { - status == ResultStatus.OK && data!= null -> Success(data) - status == ResultStatus.TIMEOUT && data!= null -> Timeout(data) + status == ResultStatus.OK && data != null -> Success(data) + status == ResultStatus.TIMEOUT && data != null -> Timeout(data) else -> Error(message, exception) } resultObject.apply { @@ -161,8 +161,10 @@ sealed class ResultSupreme { is Timeout -> ResultStatus.TIMEOUT is Error -> ResultStatus.UNKNOWN } - return create(status, newData, message ?: result.message, - exception ?: result.exception) + return create( + status, newData, message ?: result.message, + exception ?: result.exception + ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index 5e383976f..d8a932dd4 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -48,9 +48,9 @@ import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.AppTag import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type -import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.PWAManagerModule import foundation.e.apps.utils.modules.PreferenceManagerModule @@ -363,9 +363,11 @@ class FusedAPIImpl @Inject constructor( suspend fun getPlayStoreApps(browseUrl: String, authData: AuthData): ResultSupreme> { val list = mutableListOf() val status = runCodeBlockWithTimeout({ - list.addAll(gPlayAPIRepository.listApps(browseUrl, authData).map { app -> - app.transformToFusedApp() - }) + list.addAll( + gPlayAPIRepository.listApps(browseUrl, authData).map { app -> + app.transformToFusedApp() + } + ) }) return ResultSupreme.create(status, list) } @@ -510,7 +512,7 @@ class FusedAPIImpl @Inject constructor( origin: Origin ): Pair { - var response : FusedApp? = null + var response: FusedApp? = null val status = runCodeBlockWithTimeout({ response = if (origin == Origin.CLEANAPK) { @@ -608,7 +610,6 @@ class FusedAPIImpl @Inject constructor( apiStatus = ResultStatus.UNKNOWN }) - /* * Try within timeout limit to get PWA categories */ diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 1bc89fbe9..35986658b 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -19,18 +19,18 @@ package foundation.e.apps.api.fused 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.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster -import com.aurora.gplayapi.data.models.App import foundation.e.apps.api.ResultSupreme import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedCategory import foundation.e.apps.api.fused.data.FusedHome import foundation.e.apps.manager.database.fusedDownload.FusedDownload -import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt index 7b0f869f2..254b28a80 100644 --- a/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt @@ -195,8 +195,7 @@ class GPlayAPIImpl @Inject constructor( val categoryHelper = CategoryHelper(authData).using(gPlayHttpClient) if (currentStreamBundle.streamClusters.isEmpty()) { categoryHelper.getSubCategoryBundle(homeUrl) - } - else { + } else { categoryHelper.getSubCategoryBundle(currentStreamBundle.streamNextPageUrl) } } @@ -245,8 +244,7 @@ class GPlayAPIImpl @Inject constructor( val browseResponse = streamHelper.getBrowseStreamResponse(this.clusterBrowseUrl) if (browseResponse.contentsUrl.isNotEmpty()) { return@withContext streamHelper.getNextStreamCluster(browseResponse.contentsUrl) - } - else if (browseResponse.hasBrowseTab()) { + } else if (browseResponse.hasBrowseTab()) { return@withContext streamHelper.getNextStreamCluster(browseResponse.browseTab.listUrl) } } diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index 447263e46..e3fb78746 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -60,9 +60,9 @@ import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.utils.modules.PWAManagerModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt index 31ace4667..21562084e 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListFragment.kt @@ -43,11 +43,10 @@ import foundation.e.apps.applicationlist.model.ApplicationListRVAdapter import foundation.e.apps.databinding.FragmentApplicationListBinding import foundation.e.apps.manager.download.data.DownloadProgress import foundation.e.apps.manager.pkg.PkgManagerModule -import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.PWAManagerModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index a176df206..76aba8e89 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -21,7 +21,6 @@ package foundation.e.apps.home import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -46,9 +45,9 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/foundation/e/apps/manager/download/data/DownloadProgressLD.kt b/app/src/main/java/foundation/e/apps/manager/download/data/DownloadProgressLD.kt index b606f3b9b..ecee78faa 100644 --- a/app/src/main/java/foundation/e/apps/manager/download/data/DownloadProgressLD.kt +++ b/app/src/main/java/foundation/e/apps/manager/download/data/DownloadProgressLD.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.coroutines.CoroutineContext diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index 3d66b351c..732389e54 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -54,8 +54,8 @@ import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.PWAManagerModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject @@ -174,7 +174,6 @@ class SearchFragment : searchViewModel.searchResult.apply { value = Pair(searchList, value?.second) } } - /* * Explanation of double observers in HomeFragment.kt * Modified to check and search only if searchText in not blank, to prevent blank search. diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 6dff83206..b5b6be898 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -47,9 +47,9 @@ import foundation.e.apps.updates.manager.UpdatesWorkManager import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.User -import foundation.e.apps.utils.parentFragment.TimeoutFragment import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.modules.PWAManagerModule +import foundation.e.apps.utils.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt b/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt index f9f3c5de7..6fb9ceebe 100644 --- a/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt +++ b/app/src/main/java/foundation/e/apps/utils/enums/ResultStatus.kt @@ -4,4 +4,4 @@ enum class ResultStatus { OK, TIMEOUT, UNKNOWN, -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt index 2aa3d485f..7900b2380 100644 --- a/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/utils/parentFragment/TimeoutFragment.kt @@ -31,7 +31,7 @@ import foundation.e.apps.R * for network calls exceeding timeout limit. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ -abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { +abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { /* * Alert dialog to show to user if App Lounge times out. @@ -159,17 +159,17 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { * Set buttons. */ positiveButtonText?.let { - setPositiveButton(it) {_, _ -> + setPositiveButton(it) { _, _ -> positiveButtonBlock?.invoke() } } negativeButtonText?.let { - setNegativeButton(it) {_, _ -> + setNegativeButton(it) { _, _ -> negativeButtonBlock?.invoke() } } neutralButtonText?.let { - setNeutralButton(it) {_, _ -> + setNeutralButton(it) { _, _ -> neutralButtonBlock?.invoke() } } @@ -211,4 +211,4 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int): Fragment(layoutId) { } catch (_: Exception) {} } } -} \ No newline at end of file +} -- GitLab From 478c696374e1763ddb186102a73cf91553c652dc Mon Sep 17 00:00:00 2001 From: SayantanRC Date: Thu, 9 Jun 2022 16:16:55 +0530 Subject: [PATCH 35/35] issue_5131_2: remove commented out code --- .../e/apps/api/fused/FusedAPIImpl.kt | 23 ----------------- .../e/apps/api/fused/FusedAPIRepository.kt | 5 ---- .../ApplicationListViewModel.kt | 25 ------------------- 3 files changed, 53 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt index d8a932dd4..dc31a00e8 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt @@ -276,29 +276,6 @@ class FusedAPIImpl @Inject constructor( fusedDownload.downloadURLList = list } - // This method is no longer used - /*suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { - val preferredApplicationType = preferenceManagerModule.preferredApplicationType() - - if (preferredApplicationType != "any") { - val response = if (preferredApplicationType == "open") { - getOpenSourceAppsResponse(category) - } else { - getPWAAppsResponse(category) - } - response?.apps?.forEach { - it.updateStatus() - it.updateType() - } - return response?.apps - } else { - val listApps = gPlayAPIRepository.listApps(browseUrl, authData) - return listApps.map { app -> - app.transformToFusedApp() - } - } - }*/ - suspend fun getPWAApps(category: String): ResultSupreme> { val list = mutableListOf() val status = runCodeBlockWithTimeout({ diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt index 35986658b..720e48526 100644 --- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt @@ -111,11 +111,6 @@ class FusedAPIRepository @Inject constructor( return fusedAPIImpl.getSearchResults(query, authData) } - // This method is no longer used - /*suspend fun listApps(category: String, browseUrl: String, authData: AuthData): List? { - return fusedAPIImpl.listApps(category, browseUrl, authData) - }*/ - suspend fun getNextStreamBundle( authData: AuthData, homeUrl: String, diff --git a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt index 2ae4cb1ea..0d689ef75 100644 --- a/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/applicationlist/ApplicationListViewModel.kt @@ -94,31 +94,6 @@ class ApplicationListViewModel @Inject constructor( } appListLiveData.postValue(appsListData) - - /*if (!appsListData.isSuccess()) { - appListLiveData.postValue(appsListData) - return@launch - } - - val applicationDetailsWithStatus = if (!source.contentEquals("PWA")) { - *//* - * Optimization: packageNames were not used anywhere else, - * hence moved here. - *//* - val packageNames = appsListData.data!!.map { it.package_name } - val pair = fusedAPIRepository.getApplicationDetails( - packageNames, authData, - getOrigin(source) - ) - ResultSupreme.create(pair.second, pair.first) - } else { - *//* - * Optimization: Old code was same as the one called above. - *//* - appsListData - } - - appListLiveData.postValue(applicationDetailsWithStatus)*/ } } -- GitLab