diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt index 9568f1e96573bb7cef1585d16281ebe601cc3f30..f0812b4a1c584ce29767e0977f4195f73c88bd25 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt @@ -65,4 +65,7 @@ interface ContentRatingDao { @Query("SELECT * FROM ContentRatingEntity WHERE packageName = :packageName LIMIT 1") suspend fun getContentRating(packageName: String): ContentRatingEntity? -} \ No newline at end of file + @Query("SELECT COUNT(*) FROM ContentRatingEntity WHERE packageName IS NOT NULL AND TRIM(packageName) != ''") + suspend fun getContentRatingCount(): Int + +} diff --git a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt index 8b0b2858885bda6e3d1c19e308b41e194a1ce139..994289f21415a5960a18b8b0b4fe77b45d1f1ba7 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -46,11 +46,13 @@ class ValidateAppAgeLimitUseCase @Inject constructor( const val KEY_ANTI_FEATURES_NSFW = "NSFW" } + private val ageGroup: Age + get() = parentalControlRepository.getSelectedAgeGroup() + suspend operator fun invoke(app: AppInstall): ResultSupreme { - val ageGroup = parentalControlRepository.getSelectedAgeGroup() return when { - isParentalControlDisabled(ageGroup) -> ResultSupreme.Success( + isParentalControlDisabled() -> ResultSupreme.Success( data = ContentRatingValidity(true) ) @@ -125,7 +127,9 @@ class ValidateAppAgeLimitUseCase @Inject constructor( return app.contentRating.id.isNotEmpty() && allowedAgeRatings.contains(app.contentRating.id) } - private fun isParentalControlDisabled(ageGroup: Age) = ageGroup == Age.PARENTAL_CONTROL_DISABLED + fun isParentalControlDisabled(): Boolean { + return ageGroup == Age.PARENTAL_CONTROL_DISABLED + } private suspend fun verifyContentRatingExists(app: AppInstall): Boolean { diff --git a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt index f7cda6a029410cdd4cb795333369b4692c413ad3..c82240b4047150b1629bb43c0c41418ee08d4eee 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -43,6 +43,7 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.login.AuthenticatorRepository +import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.ContentRatingEntity import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository @@ -51,6 +52,7 @@ import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.domain.ValidateAppAgeLimitUseCase import foundation.e.apps.domain.model.ContentRatingValidity import foundation.e.apps.install.pkg.AppLoungePackageManager +import foundation.e.apps.utils.isNetworkAvailable import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -133,7 +135,7 @@ class AgeRatingProvider : ContentProvider() { withContext(IO) { try { if (packageNames.isEmpty()) return@withContext cursor - if (!setupAuthDataIfExists()) return@withContext null + canSetupAuthData() ensureAgeGroupDataExists() compileAppBlockList(cursor, packageNames) @@ -192,16 +194,14 @@ class AgeRatingProvider : ContentProvider() { } /** - * Return true if valid AuthData could be fetched from data store, false otherwise. + * Setup AuthData for other APIs to access, + * if user has logged in with Google or Anonymous mode. */ - private fun setupAuthDataIfExists(): Boolean { + private fun canSetupAuthData() { val authData = dataStoreManager.getAuthData() if (authData.email.isNotBlank() && authData.authToken.isNotBlank()) { authenticatorRepository.gplayAuth = authData - return true } - Timber.e("Blank AuthData, cannot fetch ratings from provider.") - return false } private suspend fun isAppValidRegardingAge(packageName: String): Boolean? { @@ -219,13 +219,15 @@ class AgeRatingProvider : ContentProvider() { validateResult: ResultSupreme, packageName: String ) { - if (validateResult.data?.isValid == false) { - val ratingId = validateResult.data?.contentRating?.id ?: "" - val ratingTitle = validateResult.data?.contentRating?.title ?: "" - contentRatingDao.insertContentRating( - ContentRatingEntity(packageName, ratingId, ratingTitle) - ) - } + val resultData = validateResult.data ?: return + + if (resultData.isValid || resultData.contentRating == null) return + + val ratingId = resultData.contentRating.id + val ratingTitle = resultData.contentRating.title + contentRatingDao.insertContentRating( + ContentRatingEntity(packageName, ratingId, ratingTitle) + ) } private suspend fun isAppValidRegardingNSWF(packageName: String): Boolean { @@ -237,8 +239,10 @@ class AgeRatingProvider : ContentProvider() { return validateResult.data?.isValid ?: false } - private suspend fun shouldAllow(packageName: String): Boolean { + private suspend fun shouldAllowToRun(packageName: String): Boolean { return when { + validateAppAgeLimitUseCase.isParentalControlDisabled() -> true + !isInitialized() -> false !isAppValidRegardingNSWF(packageName) -> false isAppValidRegardingAge(packageName) == false -> false else -> true @@ -252,7 +256,7 @@ class AgeRatingProvider : ContentProvider() { withContext(IO) { val validityList = packageNames.map { packageName -> async { - shouldAllow(packageName) + shouldAllowToRun(packageName) } }.awaitAll() validityList.forEachIndexed { index: Int, isValid: Boolean? -> @@ -266,6 +270,27 @@ class AgeRatingProvider : ContentProvider() { } } + private suspend fun hasContentRatings(): Boolean { + return contentRatingDao.getContentRatingCount() > 0 + } + + private fun hasNetwork(): Boolean { + return context?.isNetworkAvailable() ?: false + } + + private fun hasAuthData(): Boolean { + return try { + authenticatorRepository.gplayAuth != null + } catch (e: GPlayLoginException) { + Timber.e("No AuthData to check content rating") + false + } + } + + private suspend fun isInitialized(): Boolean { + return (hasNetwork() && hasAuthData()) || hasContentRatings() + } + override fun onCreate(): Boolean { val appContext = context?.applicationContext ?: error("Null context in ${this::class.java.name}") val hiltEntryPoint =