From 6bdc8ab39d7eb6ae16c74ef6355aadae02a3a4f0 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Wed, 10 Jul 2024 16:47:20 +0530 Subject: [PATCH 1/4] block all apps if no network or table is empty --- .../data/parentalcontrol/ContentRatingDao.kt | 3 ++ .../e/apps/provider/AgeRatingProvider.kt | 39 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) 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 9568f1e96..5c6cb174f 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? + @Query("SELECT COUNT(*) FROM ContentRatingEntity WHERE packageName IS NOT NULL AND TRIM(packageName) != ''") + suspend fun getContentRatingCount(): Int + } \ No newline at end of file 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 f7cda6a02..3440fe008 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -51,6 +51,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 +134,7 @@ class AgeRatingProvider : ContentProvider() { withContext(IO) { try { if (packageNames.isEmpty()) return@withContext cursor - if (!setupAuthDataIfExists()) return@withContext null + setupAuthDataIfExists() ensureAgeGroupDataExists() compileAppBlockList(cursor, packageNames) @@ -205,6 +206,7 @@ class AgeRatingProvider : ContentProvider() { } private suspend fun isAppValidRegardingAge(packageName: String): Boolean? { + if (!isAuthDataPresent()) return null val fakeAppInstall = AppInstall( packageName = packageName, origin = Origin.GPLAY @@ -219,13 +221,16 @@ 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) return + resultData.contentRating ?: return + + val ratingId = resultData.contentRating.id + val ratingTitle = resultData.contentRating.title + contentRatingDao.insertContentRating( + ContentRatingEntity(packageName, ratingId, ratingTitle) + ) } private suspend fun isAppValidRegardingNSWF(packageName: String): Boolean { @@ -239,6 +244,7 @@ class AgeRatingProvider : ContentProvider() { private suspend fun shouldAllow(packageName: String): Boolean { return when { + !isNetworkAvailable() && !isTableInitialized() -> false !isAppValidRegardingNSWF(packageName) -> false isAppValidRegardingAge(packageName) == false -> false else -> true @@ -266,6 +272,23 @@ class AgeRatingProvider : ContentProvider() { } } + private suspend fun isTableInitialized(): Boolean { + return contentRatingDao.getContentRatingCount() > 0 + } + + private fun isNetworkAvailable(): Boolean { + return context?.isNetworkAvailable() ?: false + } + + private fun isAuthDataPresent(): Boolean { + return try { + authenticatorRepository.gplayAuth != null + } catch (e: Exception) { + Timber.e("No AuthData to check content rating") + false + } + } + override fun onCreate(): Boolean { val appContext = context?.applicationContext ?: error("Null context in ${this::class.java.name}") val hiltEntryPoint = -- GitLab From f13f7547f63a15fcb4e5793b9c72fc48f1cad25d Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Wed, 10 Jul 2024 16:49:28 +0530 Subject: [PATCH 2/4] Don't return anything from setupAuthDataIfExists --- .../java/foundation/e/apps/provider/AgeRatingProvider.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 3440fe008..46b9fe587 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -195,14 +195,11 @@ class AgeRatingProvider : ContentProvider() { /** * Return true if valid AuthData could be fetched from data store, false otherwise. */ - private fun setupAuthDataIfExists(): Boolean { + private fun setupAuthDataIfExists() { 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? { -- GitLab From 5d8374b727fb149ef95cbe86dd7499fe3a156378 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Wed, 10 Jul 2024 18:57:36 +0530 Subject: [PATCH 3/4] multiple renamings and minor refactors --- .../data/parentalcontrol/ContentRatingDao.kt | 2 +- .../e/apps/provider/AgeRatingProvider.kt | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) 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 5c6cb174f..f0812b4a1 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 @@ -68,4 +68,4 @@ interface ContentRatingDao { @Query("SELECT COUNT(*) FROM ContentRatingEntity WHERE packageName IS NOT NULL AND TRIM(packageName) != ''") suspend fun getContentRatingCount(): Int -} \ No newline at end of file +} 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 46b9fe587..413be2330 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 @@ -134,7 +135,7 @@ class AgeRatingProvider : ContentProvider() { withContext(IO) { try { if (packageNames.isEmpty()) return@withContext cursor - setupAuthDataIfExists() + canSetupAuthData() ensureAgeGroupDataExists() compileAppBlockList(cursor, packageNames) @@ -193,9 +194,10 @@ 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() { + private fun canSetupAuthData() { val authData = dataStoreManager.getAuthData() if (authData.email.isNotBlank() && authData.authToken.isNotBlank()) { authenticatorRepository.gplayAuth = authData @@ -203,7 +205,6 @@ class AgeRatingProvider : ContentProvider() { } private suspend fun isAppValidRegardingAge(packageName: String): Boolean? { - if (!isAuthDataPresent()) return null val fakeAppInstall = AppInstall( packageName = packageName, origin = Origin.GPLAY @@ -220,8 +221,7 @@ class AgeRatingProvider : ContentProvider() { ) { val resultData = validateResult.data ?: return - if (resultData.isValid) return - resultData.contentRating ?: return + if (resultData.isValid || resultData.contentRating == null) return val ratingId = resultData.contentRating.id val ratingTitle = resultData.contentRating.title @@ -239,9 +239,9 @@ class AgeRatingProvider : ContentProvider() { return validateResult.data?.isValid ?: false } - private suspend fun shouldAllow(packageName: String): Boolean { + private suspend fun shouldAllowToRun(packageName: String): Boolean { return when { - !isNetworkAvailable() && !isTableInitialized() -> false + !isInitialized() -> false !isAppValidRegardingNSWF(packageName) -> false isAppValidRegardingAge(packageName) == false -> false else -> true @@ -255,7 +255,7 @@ class AgeRatingProvider : ContentProvider() { withContext(IO) { val validityList = packageNames.map { packageName -> async { - shouldAllow(packageName) + shouldAllowToRun(packageName) } }.awaitAll() validityList.forEachIndexed { index: Int, isValid: Boolean? -> @@ -269,23 +269,27 @@ class AgeRatingProvider : ContentProvider() { } } - private suspend fun isTableInitialized(): Boolean { + private suspend fun hasContentRatings(): Boolean { return contentRatingDao.getContentRatingCount() > 0 } - private fun isNetworkAvailable(): Boolean { + private fun hasNetwork(): Boolean { return context?.isNetworkAvailable() ?: false } - private fun isAuthDataPresent(): Boolean { + private fun hasAuthData(): Boolean { return try { authenticatorRepository.gplayAuth != null - } catch (e: Exception) { + } 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 = -- GitLab From 0bef9acda067c25a058f5c20474cd1028ad2a7d1 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Wed, 10 Jul 2024 20:17:35 +0530 Subject: [PATCH 4/4] don't block apps if parental control is off --- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 10 +++++++--- .../foundation/e/apps/provider/AgeRatingProvider.kt | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) 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 8b0b28588..994289f21 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 413be2330..c82240b40 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -241,6 +241,7 @@ class AgeRatingProvider : ContentProvider() { private suspend fun shouldAllowToRun(packageName: String): Boolean { return when { + validateAppAgeLimitUseCase.isParentalControlDisabled() -> true !isInitialized() -> false !isAppValidRegardingNSWF(packageName) -> false isAppValidRegardingAge(packageName) == false -> false -- GitLab