From d1c3ceced864ca774c7d5487e6a5aa0932f2091a Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 23 May 2024 14:18:52 +0600 Subject: [PATCH 1/3] refactor: improve content rating fetching with ID The GPlayApi library version is also updated in build.gradle for this feature. --- app/build.gradle | 2 +- .../data/playstore/PlayStoreRepository.kt | 10 ++++- .../data/playstore/PlayStoreRepositoryImpl.kt | 38 ++++++++++++------- .../foundation/e/apps/di/RepositoryModule.kt | 24 ++++++++++++ .../ui/application/ApplicationFragment.kt | 25 ++++++------ .../ui/application/ApplicationViewModel.kt | 21 +++++++--- 6 files changed, 85 insertions(+), 35 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a3df29701..c324b3683 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,7 @@ dependencies { api files('libs/splitinstall-lib.jar') implementation 'foundation.e.lib:telemetry:0.0.11-alpha' - implementation "foundation.e:gplayapi:3.2.10-2" + implementation "foundation.e:gplayapi:3.2.10-3" implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.fragment:fragment-ktx:1.5.6' diff --git a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt index 50480c04e..798eb4daa 100644 --- a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt @@ -1,6 +1,5 @@ /* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! + * Copyright (C) 2024 MURENA SAS * * 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 @@ -14,6 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * */ package foundation.e.apps.data.playstore @@ -21,6 +21,7 @@ package foundation.e.apps.data.playstore import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Category +import com.aurora.gplayapi.data.models.ContentRating import com.aurora.gplayapi.data.models.File import com.aurora.gplayapi.data.models.SearchBundle import foundation.e.apps.data.StoreRepository @@ -43,4 +44,9 @@ interface PlayStoreRepository : StoreRepository { versionCode: Int, offerType: Int ): List + + suspend fun updateContentRatingWithId( + appPackage: String, + contentRating: ContentRating + ): ContentRating } diff --git a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt index 63a467e92..9fd0d305f 100644 --- a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt @@ -1,6 +1,5 @@ /* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! + * Copyright (C) 2024 MURENA SAS * * 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 @@ -14,6 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * */ package foundation.e.apps.data.playstore @@ -23,6 +23,7 @@ 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.ContentRating import com.aurora.gplayapi.data.models.File import com.aurora.gplayapi.data.models.SearchBundle import com.aurora.gplayapi.data.models.StreamCluster @@ -30,14 +31,15 @@ import com.aurora.gplayapi.helpers.AppDetailsHelper import com.aurora.gplayapi.helpers.CategoryAppsHelper import com.aurora.gplayapi.helpers.CategoryHelper import com.aurora.gplayapi.helpers.Chart +import com.aurora.gplayapi.helpers.ContentRatingHelper import com.aurora.gplayapi.helpers.PurchaseHelper import com.aurora.gplayapi.helpers.SearchHelper import com.aurora.gplayapi.helpers.TopChartsHelper import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.application.utils.CategoryType -import foundation.e.apps.data.playstore.utils.GPlayHttpClient import foundation.e.apps.data.login.AuthenticatorRepository +import foundation.e.apps.data.playstore.utils.GPlayHttpClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber @@ -78,8 +80,7 @@ class PlayStoreRepositoryImpl @Inject constructor( subBundle: MutableSet? ): Pair, MutableSet> { var authData = authenticatorRepository.gplayAuth!! - val searchHelper = - SearchHelper(authData).using(gPlayHttpClient) + val searchHelper = SearchHelper(authData).using(gPlayHttpClient) Timber.d("Fetching search result for $query, subBundle: $subBundle") @@ -115,8 +116,7 @@ class PlayStoreRepositoryImpl @Inject constructor( override suspend fun getAppsByCategory(category: String, pageUrl: String?): StreamCluster { val authData = authenticatorRepository.gplayAuth!! - val subCategoryHelper = - CategoryAppsHelper(authData).using(gPlayHttpClient) + val subCategoryHelper = CategoryAppsHelper(authData).using(gPlayHttpClient) if (!pageUrl.isNullOrEmpty()) { return subCategoryHelper.next(pageUrl) @@ -163,7 +163,8 @@ class PlayStoreRepositoryImpl @Inject constructor( } private fun getCategoryType(type: CategoryType): Category.Type { - return if (type == CategoryType.APPLICATION) Category.Type.APPLICATION else Category.Type.GAME + return if (type == CategoryType.APPLICATION) Category.Type.APPLICATION + else Category.Type.GAME } private suspend fun getTopApps( @@ -207,14 +208,23 @@ class PlayStoreRepositoryImpl @Inject constructor( withContext(Dispatchers.IO) { val purchaseHelper = PurchaseHelper(authData).using(gPlayHttpClient) downloadData.addAll( - purchaseHelper.getOnDemandModule( - packageName, - moduleName, - versionCode, - offerType - ) + purchaseHelper.getOnDemandModule(packageName, moduleName, versionCode, offerType) ) } return downloadData } + + override suspend fun updateContentRatingWithId( + appPackage: String, + contentRating: ContentRating + ): ContentRating { + val authData = authenticatorRepository.gplayAuth!! + val contentRatingHelper = ContentRatingHelper(authData) + return withContext(Dispatchers.IO) { + contentRatingHelper.updateContentRatingWithId( + appPackage, + contentRating + ) + } + } } diff --git a/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt b/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt index 204767a8f..4e43c269f 100644 --- a/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt +++ b/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2024 MURENA SAS + * + * 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.di import dagger.Binds @@ -12,6 +30,8 @@ import foundation.e.apps.data.fdroid.FdroidRepository import foundation.e.apps.data.fdroid.IFdroidRepository import foundation.e.apps.data.fusedDownload.FusedManagerImpl import foundation.e.apps.data.fusedDownload.IFusedManager +import foundation.e.apps.data.playstore.PlayStoreRepository +import foundation.e.apps.data.playstore.PlayStoreRepositoryImpl import javax.inject.Singleton @Module @@ -32,4 +52,8 @@ interface RepositoryModule { @Singleton @Binds fun getPrivacyScoreRepository(privacyScoreRepositoryImpl: PrivacyScoreRepositoryImpl): PrivacyScoreRepository + + @Singleton + @Binds + fun getPlayStoreRepository(playStoreRepository: PlayStoreRepositoryImpl): PlayStoreRepository } diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index d71d6def5..e2011f01b 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -494,23 +494,13 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun collectAppContentRatingState() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { - applicationViewModel.appContentRating.collectLatest(::updateContentRatingUi) + applicationViewModel.appContentRatingState + .collectLatest(::updateContentRatingUi) } } } - private fun updateContentRatingUi(contentRating: ContentRating) { - fun loadContentRating(contentRating: ContentRating) { - lifecycleScope.launch { - val drawable = loadContentRatingDrawable(contentRating.artwork.url) - displayRating(contentRating, drawable) - } - } - - fun hideContentRating() { - binding.ratingsInclude.appContentRatingLayout.visibility = View.GONE - } - + private suspend fun updateContentRatingUi(contentRating: ContentRating) { if (contentRating.isValid()) { loadContentRating(contentRating) } else { @@ -518,6 +508,15 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { } } + private suspend fun loadContentRating(contentRating: ContentRating) { + val drawable = loadContentRatingDrawable(contentRating.artwork.url) + displayRating(contentRating, drawable) + } + + private fun hideContentRating() { + binding.ratingsInclude.appContentRatingLayout.visibility = View.GONE + } + private fun displayRating(contentRating: ContentRating, drawable: Drawable?) { binding.ratingsInclude.apply { appContentRatingTitle.text = contentRating.title diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index 8d1815588..770778b87 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -36,6 +36,7 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException +import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.download.data.DownloadProgressLD import foundation.e.apps.ui.application.ShareButtonVisibilityState.Hidden @@ -53,6 +54,7 @@ class ApplicationViewModel @Inject constructor( downloadProgressLD: DownloadProgressLD, private val applicationRepository: ApplicationRepository, private val fusedManagerRepository: FusedManagerRepository, + private val playStoreRepository: PlayStoreRepository, ) : LoadingViewModel() { val application: MutableLiveData> = MutableLiveData() @@ -64,8 +66,8 @@ class ApplicationViewModel @Inject constructor( private val _shareButtonVisibilityState = MutableStateFlow(Hidden) val shareButtonVisibilityState = _shareButtonVisibilityState.asStateFlow() - private val _appContentRating = MutableStateFlow(ContentRating()) - val appContentRating = _appContentRating.asStateFlow() + private val _appContentRatingState = MutableStateFlow(ContentRating()) + val appContentRatingState = _appContentRatingState.asStateFlow() fun loadData( id: String, @@ -119,7 +121,7 @@ class ApplicationViewModel @Inject constructor( application.postValue(appData) updateShareVisibilityState(appData.first.shareUri.toString()) - updateAppContentRatingState(appData.first.contentRating) + updateAppContentRatingState(packageName, appData.first.contentRating) val status = appData.second @@ -146,8 +148,17 @@ class ApplicationViewModel @Inject constructor( } } - private fun updateAppContentRatingState(value: ContentRating) { - _appContentRating.update { value } + private suspend fun updateAppContentRatingState( + packageName: String, + contentRating: ContentRating + ) { + // Initially update the state without ID to show the UI immediately + _appContentRatingState.update { contentRating } + + val ratingWithId = playStoreRepository.updateContentRatingWithId(packageName, contentRating) + + // Later, update silently not affecting the UI + _appContentRatingState.update { contentRating.copy(id = ratingWithId.id) } } private fun updateShareVisibilityState(shareUri: String) { -- GitLab From 46dc8e0ad80c14f9535530c079e73355f5a92b67 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 23 May 2024 14:27:55 +0600 Subject: [PATCH 2/3] refactor: update formatting --- .../foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt index 9fd0d305f..d97181464 100644 --- a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepositoryImpl.kt @@ -220,6 +220,7 @@ class PlayStoreRepositoryImpl @Inject constructor( ): ContentRating { val authData = authenticatorRepository.gplayAuth!! val contentRatingHelper = ContentRatingHelper(authData) + return withContext(Dispatchers.IO) { contentRatingHelper.updateContentRatingWithId( appPackage, -- GitLab From 1b9f19107b89fdacec02ecc0e50d25f9db154dc9 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 23 May 2024 16:28:53 +0600 Subject: [PATCH 3/3] refactor: update comment --- .../foundation/e/apps/ui/application/ApplicationViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index 770778b87..57b2be86f 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -157,7 +157,7 @@ class ApplicationViewModel @Inject constructor( val ratingWithId = playStoreRepository.updateContentRatingWithId(packageName, contentRating) - // Later, update silently not affecting the UI + // Later, update with a new rating; no visual change in the UI _appContentRatingState.update { contentRating.copy(id = ratingWithId.id) } } -- GitLab