From b5dd7325dd1c3db85e0f1fabd65336498e911b93 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:13:18 +0530 Subject: [PATCH 01/22] better logging --- .../foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt | 5 ++--- 1 file changed, 2 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 05cbe5c93..2bd91f33b 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -50,7 +50,6 @@ class ValidateAppAgeLimitUseCase @Inject constructor( isKnownNsfwApp(app) -> ResultSupreme.Success(data = false) isCleanApkApp(app) -> ResultSupreme.Success(!isNsfwAppByCleanApkApi(app)) isWhiteListedCleanApkApp(app) -> ResultSupreme.Success(data = true) - // Check for GPlay apps now hasNoContentRatingOnGPlay(app) -> ResultSupreme.Error() else -> validateAgeLimit(ageGroup, app) } @@ -86,8 +85,8 @@ class ValidateAppAgeLimitUseCase @Inject constructor( gPlayContentRatingRepository.contentRatingGroups.find { it.id == ageGroup.toString() } Timber.d( - "Selected age group: $ageGroup \n" + - "Content rating: ${app.contentRating.id} \n" + + "${app.packageName} - Content rating: ${app.contentRating.id} \n" + + "Selected age group: $ageGroup \n" + "Allowed content rating: $allowedContentRating" ) -- GitLab From d801c92836a4fe3343ddbeff6d4911281bb87859 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:17:23 +0530 Subject: [PATCH 02/22] DB definition and DI --- .../e/apps/data/database/AppDatabase.kt | 19 +++++- .../apps/data/parentalcontrol/AgeRatingDAO.kt | 65 +++++++++++++++++++ .../googleplay/GPlayContentRatingGroup.kt | 7 +- .../java/foundation/e/apps/di/DaoModule.kt | 6 ++ 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt diff --git a/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt b/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt index 69c179b19..0628a050f 100644 --- a/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt +++ b/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt @@ -4,24 +4,39 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +import foundation.e.apps.data.database.install.AppInstallConverter import foundation.e.apps.data.exodus.Tracker import foundation.e.apps.data.exodus.TrackerDao import foundation.e.apps.data.faultyApps.FaultyApp import foundation.e.apps.data.faultyApps.FaultyAppDao import foundation.e.apps.data.fdroid.FdroidDao import foundation.e.apps.data.fdroid.models.FdroidEntity +import foundation.e.apps.data.parentalcontrol.AgeRating +import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.FDroidNsfwApp +import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup @Database( - entities = [Tracker::class, FdroidEntity::class, FaultyApp::class], - version = 4, + entities = [ + Tracker::class, + FdroidEntity::class, + FaultyApp::class, + AgeRating::class, + FDroidNsfwApp::class, + GPlayContentRatingGroup::class, + ], + version = 5, exportSchema = false ) +@TypeConverters(AppInstallConverter::class) abstract class AppDatabase : RoomDatabase() { abstract fun trackerDao(): TrackerDao abstract fun fdroidDao(): FdroidDao abstract fun faultyAppsDao(): FaultyAppDao + abstract fun ageRatingDao(): AgeRatingDAO companion object { private lateinit var INSTANCE: AppDatabase diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt new file mode 100644 index 000000000..6ab420647 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt @@ -0,0 +1,65 @@ +/* + * 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.data.parentalcontrol + +import androidx.room.Dao +import androidx.room.Entity +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.PrimaryKey +import androidx.room.Query +import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup + +@Entity +data class AgeRating( + @PrimaryKey val packageName: String, + val ratingId: String, + val ratingTitle: String, +) + +@Entity +data class FDroidNsfwApp( + @PrimaryKey val packageName: String +) + +@Dao +interface AgeRatingDAO { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAgeGroups(ageGroups: List) + + @Query("SELECT * FROM GPlayContentRatingGroup") + suspend fun getAllAgeGroups(): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertFDroidNsfwApp(nsfwApps: List) + + @Query("SELECT * FROM FDroidNsfwApp") + suspend fun getAllFDroidNsfwApp(): List + + @Query("DELETE FROM FDroidNsfwApp") + suspend fun clearFDroidNsfwApps() + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAgeRating(ageRating: AgeRating) + + @Query("SELECT * FROM AgeRating WHERE packageName = :packageName LIMIT 1") + suspend fun getAgeRating(packageName: String): AgeRating? + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingGroup.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingGroup.kt index 708e1f278..a7e7bec49 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingGroup.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingGroup.kt @@ -18,11 +18,14 @@ package foundation.e.apps.data.parentalcontrol.googleplay +import androidx.room.Entity +import androidx.room.PrimaryKey import com.squareup.moshi.Json +@Entity data class GPlayContentRatingGroup( - val id: String, + @PrimaryKey val id: String, @Json(name = "age_group") val ageGroup: String, - var ratings: List + var ratings: List = listOf() ) diff --git a/app/src/main/java/foundation/e/apps/di/DaoModule.kt b/app/src/main/java/foundation/e/apps/di/DaoModule.kt index 738869c22..05b3baf86 100644 --- a/app/src/main/java/foundation/e/apps/di/DaoModule.kt +++ b/app/src/main/java/foundation/e/apps/di/DaoModule.kt @@ -10,6 +10,7 @@ import foundation.e.apps.data.database.AppDatabase import foundation.e.apps.data.exodus.TrackerDao import foundation.e.apps.data.faultyApps.FaultyAppDao import foundation.e.apps.data.fdroid.FdroidDao +import foundation.e.apps.data.parentalcontrol.AgeRatingDAO @InstallIn(SingletonComponent::class) @Module @@ -28,4 +29,9 @@ object DaoModule { fun getFaultyAppsDao(@ApplicationContext context: Context): FaultyAppDao { return AppDatabase.getInstance(context).faultyAppsDao() } + + @Provides + fun getAgeRatingDao(@ApplicationContext context: Context): AgeRatingDAO { + return AppDatabase.getInstance(context).ageRatingDao() + } } -- GitLab From 45c5fc23b92ab35b31ec6ad059069287ba7fc823 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:27:07 +0530 Subject: [PATCH 03/22] FDroidAntiFeatureRepository - store and retrieve from db --- .../fdroid/FDroidAntiFeatureRepository.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt index 5043d8acc..48a7b0289 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt @@ -18,6 +18,8 @@ package foundation.e.apps.data.parentalcontrol.fdroid +import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.FDroidNsfwApp import javax.inject.Inject import javax.inject.Singleton @@ -26,11 +28,18 @@ class FDroidAntiFeatureRepository @Inject constructor( private val fDroidMonitorApi: FDroidMonitorApi, + private val ageRatingDAO: AgeRatingDAO, ) { var fDroidNsfwApps = listOf() private set suspend fun fetchNsfwApps() { - fDroidNsfwApps = fDroidMonitorApi.getMonitorData().body()?.getNsfwApps() ?: emptyList() + fDroidNsfwApps = runCatching { + fDroidMonitorApi.getMonitorData().body()?.getNsfwApps()?.also { + ageRatingDAO.insertFDroidNsfwApp(it.map { FDroidNsfwApp(it) }) + } ?: emptyList() + }.getOrElse { + ageRatingDAO.getAllFDroidNsfwApp().map { it.packageName } + } } } -- GitLab From bb373baa55070aef6a41b8151947d172d892a12d Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:28:18 +0530 Subject: [PATCH 04/22] GPlayContentRatingRepository - store and retrieve from db --- .../googleplay/GPlayContentRatingRepository.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt index 28d03c09d..aa1315c9d 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt @@ -20,6 +20,7 @@ package foundation.e.apps.data.parentalcontrol.googleplay import com.aurora.gplayapi.data.models.ContentRating import foundation.e.apps.data.handleNetworkResult +import foundation.e.apps.data.parentalcontrol.AgeRatingDAO import foundation.e.apps.data.playstore.PlayStoreRepository import javax.inject.Inject import javax.inject.Singleton @@ -28,6 +29,7 @@ import javax.inject.Singleton class GPlayContentRatingRepository @Inject constructor( private val ageGroupApi: AgeGroupApi, private val playStoreRepository: PlayStoreRepository, + private val ageRatingDAO: AgeRatingDAO, ) { private var _contentRatingGroups = listOf() @@ -35,9 +37,12 @@ class GPlayContentRatingRepository @Inject constructor( get() = _contentRatingGroups suspend fun fetchContentRatingData() { - val response = ageGroupApi.getDefinedAgeGroups() - if (response.isSuccessful) { - _contentRatingGroups = response.body() ?: emptyList() + _contentRatingGroups = runCatching { + ageGroupApi.getDefinedAgeGroups().body()?.also { + ageRatingDAO.insertAgeGroups(it) + } ?: emptyList() + }.getOrElse { + ageRatingDAO.getAllAgeGroups() } } -- GitLab From b55337d3c3b25552736416cc645e5e8b782710fa Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:29:19 +0530 Subject: [PATCH 05/22] ValidateAppAgeLimitUseCase - store and retrieve from db --- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 2bd91f33b..f1a1ae37b 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -18,6 +18,7 @@ package foundation.e.apps.domain +import com.aurora.gplayapi.data.models.ContentRating import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.apps.AppsApi import foundation.e.apps.data.parentalcontrol.Age @@ -26,6 +27,8 @@ import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.Type import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository +import foundation.e.apps.data.parentalcontrol.AgeRating +import foundation.e.apps.data.parentalcontrol.AgeRatingDAO import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository import timber.log.Timber @@ -36,6 +39,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private val fDroidAntiFeatureRepository: FDroidAntiFeatureRepository, private val parentalControlRepository: ParentalControlRepository, private val appsApi: AppsApi, + private val ageRatingDAO: AgeRatingDAO, ) { companion object { @@ -111,7 +115,16 @@ class ValidateAppAgeLimitUseCase @Inject constructor( if (app.contentRating.id.isEmpty()) { gPlayContentRatingRepository.getEnglishContentRating(app.packageName)?.run { Timber.d("Updating content rating for package: ${app.packageName}") + app.contentRating = this + ageRatingDAO.insertAgeRating(AgeRating(app.packageName, this.id, this.title)) + } ?: ageRatingDAO.getAgeRating(packageName = app.packageName).apply { + Timber.d("Content rating from DB for: ${app.packageName} - ${this?.ratingId}") + + app.contentRating = ContentRating( + id = this?.ratingId ?: "", + title = this?.ratingTitle ?: "", + ) } } -- GitLab From 6d697154108d7da73095e04f9ee7d505fd24109c Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 18:42:21 +0530 Subject: [PATCH 06/22] minor tweaks --- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 f1a1ae37b..3cc66297d 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -118,14 +118,14 @@ class ValidateAppAgeLimitUseCase @Inject constructor( app.contentRating = this ageRatingDAO.insertAgeRating(AgeRating(app.packageName, this.id, this.title)) - } ?: ageRatingDAO.getAgeRating(packageName = app.packageName).apply { - Timber.d("Content rating from DB for: ${app.packageName} - ${this?.ratingId}") + } ?: ageRatingDAO.getAgeRating(packageName = app.packageName)?.apply { + Timber.d("Content rating from DB for: ${app.packageName} - ${this.ratingId}") - app.contentRating = ContentRating( - id = this?.ratingId ?: "", - title = this?.ratingTitle ?: "", + app.contentRating = ContentRating ( + id = this.ratingId, + title = this.ratingTitle, ) - } + } ?: Timber.d("No content rating for: ${app.packageName}") } return app.contentRating.title.isNotEmpty() && -- GitLab From 15204ccae3662d26d3fda601de7b78fcf03cccaa Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 28 Jun 2024 19:31:48 +0530 Subject: [PATCH 07/22] converter test --- .../install/AppInstallConverterTest.kt | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt diff --git a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt new file mode 100644 index 000000000..417383d94 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt @@ -0,0 +1,136 @@ +/* + * 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.data.database.install + +import com.aurora.gplayapi.data.models.File +import kotlin.test.assertEquals +import org.junit.Before +import org.junit.Test + +class AppInstallConverterTest { + + private lateinit var converter: AppInstallConverter + + @Before + fun setup() { + converter = AppInstallConverter() + } + + @Test + fun listToJsonString() { + val list = listOf( + "Item 1", + "Item 2", + "Item 3", + ) + val convertedJson = converter.listToJsonString(list) + val expectedJson = "[\"Item 1\",\"Item 2\",\"Item 3\"]" + assertEquals(expectedJson, convertedJson) + } + + @Test + fun jsonStringToList() { + val jsonString = "[\"Item 1\",\"Item 2\",\"Item 3\"]" + val convertedList = converter.jsonStringToList(jsonString) + val expectedList = listOf( + "Item 1", + "Item 2", + "Item 3", + ) + assertEquals(expectedList, convertedList) + } + + @Test + fun listToJsonLong() { + val map = mutableMapOf( + Pair(1L, true), + Pair(2L, false), + Pair(3L, true), + ) + val convertedJson = converter.listToJsonLong(map) + val expectedJson = "{\"1\":true,\"2\":false,\"3\":true}" + assertEquals(expectedJson, convertedJson) + } + + @Test + fun jsonLongToList() { + val jsonString = "{\"1\":true,\"2\":false,\"3\":true}" + val convertedMap = converter.jsonLongToList(jsonString) + val expectedMap = mutableMapOf( + Pair(1L, true), + Pair(2L, false), + Pair(3L, true), + ) + assertEquals(expectedMap, convertedMap) + } + + @Test + fun filesToJsonString() { + val listOfFiles = listOf( + File( + id = "090gfg", + name = "File 1", + url = "example1.com", + size = 1L, + type = File.FileType.BASE, + sha1 = "abc45", + sha256 = "abcd456" + ), + File( + id = "898sas", + name = "File 2", + url = "example2.com", + size = 2L, + type = File.FileType.OBB, + sha1 = "xyz89", + sha256 = "wxyz789" + ) + ) + val convertedString = converter.filesToJsonString(listOfFiles) + val expectedString = "[{\"id\":\"090gfg\",\"name\":\"File 1\",\"url\":\"example1.com\",\"size\":1,\"type\":\"BASE\",\"sha1\":\"abc45\",\"sha256\":\"abcd456\"},{\"id\":\"898sas\",\"name\":\"File 2\",\"url\":\"example2.com\",\"size\":2,\"type\":\"OBB\",\"sha1\":\"xyz89\",\"sha256\":\"wxyz789\"}]" + assertEquals(expectedString, convertedString) + } + + @Test + fun jsonStringToFiles() { + val jsonString = "[{\"id\":\"090gfg\",\"name\":\"File 1\",\"url\":\"example1.com\",\"size\":1,\"type\":\"BASE\",\"sha1\":\"abc45\",\"sha256\":\"abcd456\"},{\"id\":\"898sas\",\"name\":\"File 2\",\"url\":\"example2.com\",\"size\":2,\"type\":\"OBB\",\"sha1\":\"xyz89\",\"sha256\":\"wxyz789\"}]" + val convertedList = converter.jsonStringToFiles(jsonString) + val expectedList = listOf( + File( + id = "090gfg", + name = "File 1", + url = "example1.com", + size = 1L, + type = File.FileType.BASE, + sha1 = "abc45", + sha256 = "abcd456" + ), + File( + id = "898sas", + name = "File 2", + url = "example2.com", + size = 2L, + type = File.FileType.OBB, + sha1 = "xyz89", + sha256 = "wxyz789" + ) + ) + assertEquals(expectedList, convertedList) + } +} \ No newline at end of file -- GitLab From f579fe80284f827cfffcce31f929acc5e1c1d952 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 14:39:31 +0530 Subject: [PATCH 08/22] Rename AgeRatingDAO to ContentRatingDao --- .../foundation/e/apps/data/database/AppDatabase.kt | 8 ++++---- .../{AgeRatingDAO.kt => ContentRatingDao.kt} | 10 +++++----- .../fdroid/FDroidAntiFeatureRepository.kt | 8 ++++---- .../googleplay/GPlayContentRatingRepository.kt | 8 ++++---- app/src/main/java/foundation/e/apps/di/DaoModule.kt | 6 +++--- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 10 +++++----- 6 files changed, 25 insertions(+), 25 deletions(-) rename app/src/main/java/foundation/e/apps/data/parentalcontrol/{AgeRatingDAO.kt => ContentRatingDao.kt} (85%) diff --git a/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt b/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt index 0628a050f..76e2cba0c 100644 --- a/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt +++ b/app/src/main/java/foundation/e/apps/data/database/AppDatabase.kt @@ -14,8 +14,8 @@ import foundation.e.apps.data.faultyApps.FaultyApp import foundation.e.apps.data.faultyApps.FaultyAppDao import foundation.e.apps.data.fdroid.FdroidDao import foundation.e.apps.data.fdroid.models.FdroidEntity -import foundation.e.apps.data.parentalcontrol.AgeRating -import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.ContentRatingEntity +import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.FDroidNsfwApp import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup @@ -24,7 +24,7 @@ import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup Tracker::class, FdroidEntity::class, FaultyApp::class, - AgeRating::class, + ContentRatingEntity::class, FDroidNsfwApp::class, GPlayContentRatingGroup::class, ], @@ -36,7 +36,7 @@ abstract class AppDatabase : RoomDatabase() { abstract fun trackerDao(): TrackerDao abstract fun fdroidDao(): FdroidDao abstract fun faultyAppsDao(): FaultyAppDao - abstract fun ageRatingDao(): AgeRatingDAO + abstract fun contentRatingDao(): ContentRatingDao companion object { private lateinit var INSTANCE: AppDatabase diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt similarity index 85% rename from app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt rename to app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt index 6ab420647..a20920ee0 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/AgeRatingDAO.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/ContentRatingDao.kt @@ -27,7 +27,7 @@ import androidx.room.Query import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup @Entity -data class AgeRating( +data class ContentRatingEntity( @PrimaryKey val packageName: String, val ratingId: String, val ratingTitle: String, @@ -39,7 +39,7 @@ data class FDroidNsfwApp( ) @Dao -interface AgeRatingDAO { +interface ContentRatingDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAgeGroups(ageGroups: List) @@ -57,9 +57,9 @@ interface AgeRatingDAO { suspend fun clearFDroidNsfwApps() @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAgeRating(ageRating: AgeRating) + suspend fun insertAgeRating(contentRatingEntity: ContentRatingEntity) - @Query("SELECT * FROM AgeRating WHERE packageName = :packageName LIMIT 1") - suspend fun getAgeRating(packageName: String): AgeRating? + @Query("SELECT * FROM ContentRatingEntity WHERE packageName = :packageName LIMIT 1") + suspend fun getContentRating(packageName: String): ContentRatingEntity? } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt index 48a7b0289..40f77d721 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt @@ -18,7 +18,7 @@ package foundation.e.apps.data.parentalcontrol.fdroid -import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.FDroidNsfwApp import javax.inject.Inject import javax.inject.Singleton @@ -28,7 +28,7 @@ class FDroidAntiFeatureRepository @Inject constructor( private val fDroidMonitorApi: FDroidMonitorApi, - private val ageRatingDAO: AgeRatingDAO, + private val contentRatingDao: ContentRatingDao, ) { var fDroidNsfwApps = listOf() private set @@ -36,10 +36,10 @@ constructor( suspend fun fetchNsfwApps() { fDroidNsfwApps = runCatching { fDroidMonitorApi.getMonitorData().body()?.getNsfwApps()?.also { - ageRatingDAO.insertFDroidNsfwApp(it.map { FDroidNsfwApp(it) }) + contentRatingDao.insertFDroidNsfwApp(it.map { FDroidNsfwApp(it) }) } ?: emptyList() }.getOrElse { - ageRatingDAO.getAllFDroidNsfwApp().map { it.packageName } + contentRatingDao.getAllFDroidNsfwApp().map { it.packageName } } } } diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt index aa1315c9d..f029dfc1e 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt @@ -20,7 +20,7 @@ package foundation.e.apps.data.parentalcontrol.googleplay import com.aurora.gplayapi.data.models.ContentRating import foundation.e.apps.data.handleNetworkResult -import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.playstore.PlayStoreRepository import javax.inject.Inject import javax.inject.Singleton @@ -29,7 +29,7 @@ import javax.inject.Singleton class GPlayContentRatingRepository @Inject constructor( private val ageGroupApi: AgeGroupApi, private val playStoreRepository: PlayStoreRepository, - private val ageRatingDAO: AgeRatingDAO, + private val contentRatingDao: ContentRatingDao, ) { private var _contentRatingGroups = listOf() @@ -39,10 +39,10 @@ class GPlayContentRatingRepository @Inject constructor( suspend fun fetchContentRatingData() { _contentRatingGroups = runCatching { ageGroupApi.getDefinedAgeGroups().body()?.also { - ageRatingDAO.insertAgeGroups(it) + contentRatingDao.insertAgeGroups(it) } ?: emptyList() }.getOrElse { - ageRatingDAO.getAllAgeGroups() + contentRatingDao.getAllAgeGroups() } } diff --git a/app/src/main/java/foundation/e/apps/di/DaoModule.kt b/app/src/main/java/foundation/e/apps/di/DaoModule.kt index 05b3baf86..c58bbde22 100644 --- a/app/src/main/java/foundation/e/apps/di/DaoModule.kt +++ b/app/src/main/java/foundation/e/apps/di/DaoModule.kt @@ -10,7 +10,7 @@ import foundation.e.apps.data.database.AppDatabase import foundation.e.apps.data.exodus.TrackerDao import foundation.e.apps.data.faultyApps.FaultyAppDao import foundation.e.apps.data.fdroid.FdroidDao -import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.ContentRatingDao @InstallIn(SingletonComponent::class) @Module @@ -31,7 +31,7 @@ object DaoModule { } @Provides - fun getAgeRatingDao(@ApplicationContext context: Context): AgeRatingDAO { - return AppDatabase.getInstance(context).ageRatingDao() + fun getContentRatingDao(@ApplicationContext context: Context): ContentRatingDao { + return AppDatabase.getInstance(context).contentRatingDao() } } 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 3cc66297d..8db2d9bc5 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -27,8 +27,8 @@ import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.Type import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository -import foundation.e.apps.data.parentalcontrol.AgeRating -import foundation.e.apps.data.parentalcontrol.AgeRatingDAO +import foundation.e.apps.data.parentalcontrol.ContentRatingEntity +import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository import timber.log.Timber @@ -39,7 +39,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private val fDroidAntiFeatureRepository: FDroidAntiFeatureRepository, private val parentalControlRepository: ParentalControlRepository, private val appsApi: AppsApi, - private val ageRatingDAO: AgeRatingDAO, + private val contentRatingDao: ContentRatingDao, ) { companion object { @@ -117,8 +117,8 @@ class ValidateAppAgeLimitUseCase @Inject constructor( Timber.d("Updating content rating for package: ${app.packageName}") app.contentRating = this - ageRatingDAO.insertAgeRating(AgeRating(app.packageName, this.id, this.title)) - } ?: ageRatingDAO.getAgeRating(packageName = app.packageName)?.apply { + contentRatingDao.insertAgeRating(ContentRatingEntity(app.packageName, this.id, this.title)) + } ?: contentRatingDao.getContentRating(packageName = app.packageName)?.apply { Timber.d("Content rating from DB for: ${app.packageName} - ${this.ratingId}") app.contentRating = ContentRating ( -- GitLab From 4492f5c2b4502c16cac5613ca52177b37a39a3a8 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 16:39:44 +0530 Subject: [PATCH 09/22] simplify ValidateAppAgeLimitUseCase --- .../apps/domain/ValidateAppAgeLimitUseCase.kt | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 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 8db2d9bc5..9dc793926 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -113,19 +113,27 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private suspend fun verifyContentRatingExists(app: AppInstall): Boolean { if (app.contentRating.id.isEmpty()) { - gPlayContentRatingRepository.getEnglishContentRating(app.packageName)?.run { - Timber.d("Updating content rating for package: ${app.packageName}") - - app.contentRating = this - contentRatingDao.insertAgeRating(ContentRatingEntity(app.packageName, this.id, this.title)) - } ?: contentRatingDao.getContentRating(packageName = app.packageName)?.apply { - Timber.d("Content rating from DB for: ${app.packageName} - ${this.ratingId}") - - app.contentRating = ContentRating ( - id = this.ratingId, - title = this.ratingTitle, + val fetchedContentRating = + gPlayContentRatingRepository.getEnglishContentRating(app.packageName) + + Timber.d("Fetched content rating - ${app.packageName} - ${fetchedContentRating?.id}") + + val newContentRating = if (fetchedContentRating != null) { + fetchedContentRating.also { + contentRatingDao.insertAgeRating( + ContentRatingEntity(app.packageName, it.id, it.title) + ) + } + } else { + val contentRatingDb = contentRatingDao.getContentRating(app.packageName) + Timber.d("Content rating from DB - ${app.packageName} - ${contentRatingDb?.ratingId}") + ContentRating ( + id = contentRatingDb?.ratingId ?: "", + title = contentRatingDb?.ratingTitle ?: "", ) - } ?: Timber.d("No content rating for: ${app.packageName}") + } + + app.contentRating = newContentRating } return app.contentRating.title.isNotEmpty() && -- GitLab From 81e1fda18c0d38ca9152360bb04e147a8bf4f73f Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 16:58:37 +0530 Subject: [PATCH 10/22] simplify FDroidAntiFeatureRepository --- .../fdroid/FDroidAntiFeatureRepository.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt index 40f77d721..711357a05 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt @@ -34,11 +34,17 @@ constructor( private set suspend fun fetchNsfwApps() { - fDroidNsfwApps = runCatching { - fDroidMonitorApi.getMonitorData().body()?.getNsfwApps()?.also { - contentRatingDao.insertFDroidNsfwApp(it.map { FDroidNsfwApp(it) }) - } ?: emptyList() - }.getOrElse { + val fetchedFDroidNsfwApps = runCatching { + fDroidMonitorApi.getMonitorData().body()?.getNsfwApps()?: emptyList() + }.getOrNull() + + fDroidNsfwApps = if (fetchedFDroidNsfwApps != null) { + fetchedFDroidNsfwApps.run { + contentRatingDao.clearFDroidNsfwApps() + contentRatingDao.insertFDroidNsfwApp(this.map { FDroidNsfwApp(it) }) + } + fetchedFDroidNsfwApps + } else { contentRatingDao.getAllFDroidNsfwApp().map { it.packageName } } } -- GitLab From 73847f4463795af4fb73156691cb7123303820da Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 17:03:24 +0530 Subject: [PATCH 11/22] simplify GPlayContentRatingRepository --- .../googleplay/GPlayContentRatingRepository.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt index f029dfc1e..bd683b629 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt @@ -37,11 +37,14 @@ class GPlayContentRatingRepository @Inject constructor( get() = _contentRatingGroups suspend fun fetchContentRatingData() { - _contentRatingGroups = runCatching { - ageGroupApi.getDefinedAgeGroups().body()?.also { - contentRatingDao.insertAgeGroups(it) - } ?: emptyList() - }.getOrElse { + val fetchedAgeGroups = runCatching { + ageGroupApi.getDefinedAgeGroups().body() + }.getOrNull() + + _contentRatingGroups = if (fetchedAgeGroups != null) { + contentRatingDao.insertAgeGroups(fetchedAgeGroups) + fetchedAgeGroups + } else { contentRatingDao.getAllAgeGroups() } } -- GitLab From b5e71f30ebee23fcfd918e7b88821f11955070f9 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 17:05:36 +0530 Subject: [PATCH 12/22] add blank line --- .../e/apps/data/database/install/AppInstallConverterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt index 417383d94..415895f48 100644 --- a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt +++ b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt @@ -133,4 +133,4 @@ class AppInstallConverterTest { ) assertEquals(expectedList, convertedList) } -} \ No newline at end of file +} -- GitLab From b77a7cc16519f5485bb21516c965ace220d5fb4f Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 17:07:22 +0530 Subject: [PATCH 13/22] further simplify ValidateAppAgeLimitUseCase --- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 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 9dc793926..2c1664103 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -118,22 +118,23 @@ class ValidateAppAgeLimitUseCase @Inject constructor( Timber.d("Fetched content rating - ${app.packageName} - ${fetchedContentRating?.id}") - val newContentRating = if (fetchedContentRating != null) { - fetchedContentRating.also { - contentRatingDao.insertAgeRating( - ContentRatingEntity(app.packageName, it.id, it.title) + if (fetchedContentRating != null) { + contentRatingDao.insertAgeRating( + ContentRatingEntity( + app.packageName, + fetchedContentRating.id, + fetchedContentRating.title ) - } + ) + app.contentRating = fetchedContentRating } else { val contentRatingDb = contentRatingDao.getContentRating(app.packageName) Timber.d("Content rating from DB - ${app.packageName} - ${contentRatingDb?.ratingId}") - ContentRating ( + app.contentRating = ContentRating ( id = contentRatingDb?.ratingId ?: "", title = contentRatingDb?.ratingTitle ?: "", ) } - - app.contentRating = newContentRating } return app.contentRating.title.isNotEmpty() && -- GitLab From a55b4009b77e6c6ac596e700d5edd5ad378bad76 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 17:35:47 +0530 Subject: [PATCH 14/22] remove empty list --- .../data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt index 711357a05..164cbdc10 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt @@ -35,7 +35,7 @@ constructor( suspend fun fetchNsfwApps() { val fetchedFDroidNsfwApps = runCatching { - fDroidMonitorApi.getMonitorData().body()?.getNsfwApps()?: emptyList() + fDroidMonitorApi.getMonitorData().body()?.getNsfwApps() }.getOrNull() fDroidNsfwApps = if (fetchedFDroidNsfwApps != null) { -- GitLab From 3a9d6249f11c401bad6c2c9c4632fe1c6cba5060 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 1 Jul 2024 17:44:33 +0530 Subject: [PATCH 15/22] change test names --- .../data/database/install/AppInstallConverterTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt index 415895f48..0fbd2ff31 100644 --- a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt +++ b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt @@ -33,7 +33,7 @@ class AppInstallConverterTest { } @Test - fun listToJsonString() { + fun `test converting kotlin list to json string`() { val list = listOf( "Item 1", "Item 2", @@ -45,7 +45,7 @@ class AppInstallConverterTest { } @Test - fun jsonStringToList() { + fun `test converting json string to kotlin list`() { val jsonString = "[\"Item 1\",\"Item 2\",\"Item 3\"]" val convertedList = converter.jsonStringToList(jsonString) val expectedList = listOf( @@ -57,7 +57,7 @@ class AppInstallConverterTest { } @Test - fun listToJsonLong() { + fun `test converting kotlin boolean map to json string`() { val map = mutableMapOf( Pair(1L, true), Pair(2L, false), @@ -69,7 +69,7 @@ class AppInstallConverterTest { } @Test - fun jsonLongToList() { + fun `test converting json string to kotlin boolean map`() { val jsonString = "{\"1\":true,\"2\":false,\"3\":true}" val convertedMap = converter.jsonLongToList(jsonString) val expectedMap = mutableMapOf( @@ -81,7 +81,7 @@ class AppInstallConverterTest { } @Test - fun filesToJsonString() { + fun `test converting File list to json string`() { val listOfFiles = listOf( File( id = "090gfg", @@ -108,7 +108,7 @@ class AppInstallConverterTest { } @Test - fun jsonStringToFiles() { + fun `test converting json string to File list`() { val jsonString = "[{\"id\":\"090gfg\",\"name\":\"File 1\",\"url\":\"example1.com\",\"size\":1,\"type\":\"BASE\",\"sha1\":\"abc45\",\"sha256\":\"abcd456\"},{\"id\":\"898sas\",\"name\":\"File 2\",\"url\":\"example2.com\",\"size\":2,\"type\":\"OBB\",\"sha1\":\"xyz89\",\"sha256\":\"wxyz789\"}]" val convertedList = converter.jsonStringToFiles(jsonString) val expectedList = listOf( -- GitLab From a7ba27466d6673d478e498c630f6a7edfbed8cd4 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 2 Jul 2024 10:49:33 +0600 Subject: [PATCH 16/22] fix: insert contentrating after installation --- .../database/install/AppInstallConverter.kt | 25 ++++++++--- .../e/apps/data/install/AppManager.kt | 2 +- .../e/apps/data/install/AppManagerImpl.kt | 20 ++++++++- .../e/apps/data/install/AppManagerWrapper.kt | 4 +- .../e/apps/data/install/models/AppInstall.kt | 8 ++-- .../data/parentalcontrol/ContentRatingDao.kt | 3 ++ .../apps/domain/ValidateAppAgeLimitUseCase.kt | 13 ++---- .../e/apps/install/pkg/PkgManagerBR.kt | 42 +++++++++---------- 8 files changed, 71 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/database/install/AppInstallConverter.kt b/app/src/main/java/foundation/e/apps/data/database/install/AppInstallConverter.kt index c2bca4df3..ab0ec0f29 100644 --- a/app/src/main/java/foundation/e/apps/data/database/install/AppInstallConverter.kt +++ b/app/src/main/java/foundation/e/apps/data/database/install/AppInstallConverter.kt @@ -1,30 +1,43 @@ package foundation.e.apps.data.database.install import androidx.room.TypeConverter +import com.aurora.gplayapi.data.models.ContentRating import com.aurora.gplayapi.data.models.File import com.google.gson.Gson import com.google.gson.reflect.TypeToken class AppInstallConverter { + private val gson = Gson() + @TypeConverter - fun listToJsonString(value: List): String = Gson().toJson(value) + fun listToJsonString(value: List): String = gson.toJson(value) @TypeConverter fun jsonStringToList(value: String) = - Gson().fromJson(value, Array::class.java).toMutableList() + gson.fromJson(value, Array::class.java).toMutableList() @TypeConverter - fun listToJsonLong(value: MutableMap): String = Gson().toJson(value) + fun listToJsonLong(value: MutableMap): String = gson.toJson(value) @TypeConverter fun jsonLongToList(value: String): MutableMap = - Gson().fromJson(value, object : TypeToken>() {}.type) + gson.fromJson(value, object : TypeToken>() {}.type) @TypeConverter - fun filesToJsonString(value: List): String = Gson().toJson(value) + fun filesToJsonString(value: List): String = gson.toJson(value) @TypeConverter fun jsonStringToFiles(value: String) = - Gson().fromJson(value, Array::class.java).toMutableList() + gson.fromJson(value, Array::class.java).toMutableList() + + @TypeConverter + fun fromContentRating(contentRating: ContentRating): String { + return gson.toJson(contentRating) + } + + @TypeConverter + fun toContentRating(name: String): ContentRating { + return gson.fromJson(name, ContentRating::class.java) + } } diff --git a/app/src/main/java/foundation/e/apps/data/install/AppManager.kt b/app/src/main/java/foundation/e/apps/data/install/AppManager.kt index f479335a4..a37de7778 100644 --- a/app/src/main/java/foundation/e/apps/data/install/AppManager.kt +++ b/app/src/main/java/foundation/e/apps/data/install/AppManager.kt @@ -39,7 +39,7 @@ interface AppManager { suspend fun installApp(appInstall: AppInstall) - suspend fun cancelDownload(appInstall: AppInstall) + suspend fun cancelDownload(appInstall: AppInstall, packageName: String = "") suspend fun getFusedDownload(downloadId: Long = 0, packageName: String = ""): AppInstall fun flushOldDownload(packageName: String) diff --git a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt index c857f5c3f..114a8b230 100644 --- a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt @@ -32,6 +32,8 @@ import foundation.e.apps.R import foundation.e.apps.data.enums.Status import foundation.e.apps.data.enums.Type import foundation.e.apps.data.install.models.AppInstall +import foundation.e.apps.data.parentalcontrol.ContentRatingDao +import foundation.e.apps.data.parentalcontrol.ContentRatingEntity import foundation.e.apps.install.download.data.DownloadProgressLD import foundation.e.apps.install.pkg.PWAManager import foundation.e.apps.install.pkg.AppLoungePackageManager @@ -58,6 +60,9 @@ class AppManagerImpl @Inject constructor( @ApplicationContext private val context: Context ) : AppManager { + @Inject + lateinit var contentRatingDao: ContentRatingDao + private val mutex = Mutex() @RequiresApi(Build.VERSION_CODES.O) @@ -89,6 +94,7 @@ class AppManagerImpl @Inject constructor( override suspend fun updateDownloadStatus(appInstall: AppInstall, status: Status) { if (status == Status.INSTALLED) { appInstall.status = status + insertContentRating(appInstall) flushOldDownload(appInstall.packageName) appInstallRepository.deleteDownload(appInstall) } else if (status == Status.INSTALLING) { @@ -99,6 +105,17 @@ class AppManagerImpl @Inject constructor( } } + private suspend fun insertContentRating(appInstall: AppInstall) { + contentRatingDao.insertAgeRating( + ContentRatingEntity( + appInstall.packageName, + appInstall.contentRating.id, + appInstall.contentRating.title + ) + ) + Timber.d("inserted age rating: ${appInstall.contentRating.title}") + } + override suspend fun downloadApp(appInstall: AppInstall) { mutex.withLock { when (appInstall.type) { @@ -137,13 +154,14 @@ class AppManagerImpl @Inject constructor( } @OptIn(DelicateCoroutinesApi::class) - override suspend fun cancelDownload(appInstall: AppInstall) { + override suspend fun cancelDownload(appInstall: AppInstall, packageName: String) { mutex.withLock { if (appInstall.id.isNotBlank()) { removeFusedDownload(appInstall) } else { Timber.d("Unable to cancel download!") } + contentRatingDao.deleteAgeRating(packageName) } } diff --git a/app/src/main/java/foundation/e/apps/data/install/AppManagerWrapper.kt b/app/src/main/java/foundation/e/apps/data/install/AppManagerWrapper.kt index 7714a8062..83cc3f3f9 100644 --- a/app/src/main/java/foundation/e/apps/data/install/AppManagerWrapper.kt +++ b/app/src/main/java/foundation/e/apps/data/install/AppManagerWrapper.kt @@ -84,8 +84,8 @@ class AppManagerWrapper @Inject constructor( return appManager.updateDownloadStatus(appInstall, status) } - suspend fun cancelDownload(appInstall: AppInstall) { - return appManager.cancelDownload(appInstall) + suspend fun cancelDownload(appInstall: AppInstall, packageName: String = "") { + return appManager.cancelDownload(appInstall, packageName) } suspend fun installationIssue(appInstall: AppInstall) { diff --git a/app/src/main/java/foundation/e/apps/data/install/models/AppInstall.kt b/app/src/main/java/foundation/e/apps/data/install/models/AppInstall.kt index 28f92b4d6..13477a2f4 100644 --- a/app/src/main/java/foundation/e/apps/data/install/models/AppInstall.kt +++ b/app/src/main/java/foundation/e/apps/data/install/models/AppInstall.kt @@ -3,8 +3,10 @@ package foundation.e.apps.data.install.models import androidx.room.Entity import androidx.room.Ignore import androidx.room.PrimaryKey +import androidx.room.TypeConverter import com.aurora.gplayapi.data.models.ContentRating import com.aurora.gplayapi.data.models.File +import com.google.gson.Gson import foundation.e.apps.data.cleanapk.CleanApkRetrofit import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.Status @@ -27,7 +29,8 @@ data class AppInstall( val isFree: Boolean = true, var appSize: Long = 0, var files: List = mutableListOf(), - var signature: String = String() + var signature: String = String(), + var contentRating: ContentRating = ContentRating() ) { @Ignore private val installingStatusList = listOf( @@ -37,9 +40,6 @@ data class AppInstall( Status.INSTALLING ) - @Ignore - var contentRating: ContentRating = ContentRating() - fun isAppInstalling() = installingStatusList.contains(status) fun isAwaiting() = status == Status.AWAITING 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 a20920ee0..76e6fcd2d 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 @@ -59,6 +59,9 @@ interface ContentRatingDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAgeRating(contentRatingEntity: ContentRatingEntity) + @Query("DELETE FROM ContentRatingEntity WHERE packageName = :packageName") + suspend fun deleteAgeRating(packageName: String) + @Query("SELECT * FROM ContentRatingEntity WHERE packageName = :packageName LIMIT 1") suspend fun getContentRating(packageName: String): ContentRatingEntity? 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 2c1664103..fad635d10 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -48,13 +48,15 @@ class ValidateAppAgeLimitUseCase @Inject constructor( suspend operator fun invoke(app: AppInstall): ResultSupreme { val ageGroup = parentalControlRepository.getSelectedAgeGroup() + val hasNoContentRatingOnGPlay = app.origin == Origin.GPLAY + && hasNoContentRatingOnGPlay(app) return when { isParentalControlDisabled(ageGroup) -> ResultSupreme.Success(data = true) isKnownNsfwApp(app) -> ResultSupreme.Success(data = false) isCleanApkApp(app) -> ResultSupreme.Success(!isNsfwAppByCleanApkApi(app)) isWhiteListedCleanApkApp(app) -> ResultSupreme.Success(data = true) - hasNoContentRatingOnGPlay(app) -> ResultSupreme.Error() + hasNoContentRatingOnGPlay -> ResultSupreme.Error() else -> validateAgeLimit(ageGroup, app) } } @@ -119,18 +121,11 @@ class ValidateAppAgeLimitUseCase @Inject constructor( Timber.d("Fetched content rating - ${app.packageName} - ${fetchedContentRating?.id}") if (fetchedContentRating != null) { - contentRatingDao.insertAgeRating( - ContentRatingEntity( - app.packageName, - fetchedContentRating.id, - fetchedContentRating.title - ) - ) app.contentRating = fetchedContentRating } else { val contentRatingDb = contentRatingDao.getContentRating(app.packageName) Timber.d("Content rating from DB - ${app.packageName} - ${contentRatingDb?.ratingId}") - app.contentRating = ContentRating ( + app.contentRating = ContentRating( id = contentRatingDb?.ratingId ?: "", title = contentRatingDb?.ratingTitle ?: "", ) diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerBR.kt b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerBR.kt index 515b27036..77748b560 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerBR.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerBR.kt @@ -54,42 +54,38 @@ open class PkgManagerBR : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val action = intent?.action if (context != null && action != null) { - val packageUid = intent.getIntExtra(Intent.EXTRA_UID, 0) val isUpdating = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) - val packages = context.packageManager.getPackagesForUid(packageUid) val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -69) - val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME) + val packageName = intent.data?.schemeSpecificPart Timber.d("onReceive: $packageName $action $extra $status") - packages?.let { pkgList -> - handlePackageList(pkgList, action, isUpdating, extra) + packageName?.let { + handlePackageList(it, action, isUpdating, extra) } } } private fun handlePackageList( - pkgList: Array, + packageName: String, action: String, isUpdating: Boolean, extra: String? ) { - pkgList.forEach { pkgName -> - when (action) { - Intent.ACTION_PACKAGE_ADDED -> { - updateDownloadStatus(pkgName) - removeFaultyAppByPackageName(pkgName) - } - - Intent.ACTION_PACKAGE_REMOVED -> { - if (!isUpdating) deleteDownload(pkgName) - removeFaultyAppByPackageName(pkgName) - } - - AppLoungePackageManager.ERROR_PACKAGE_INSTALL -> { - Timber.e("Installation failed due to error: $extra") - updateInstallationIssue(pkgName) - } + when (action) { + Intent.ACTION_PACKAGE_ADDED -> { + updateDownloadStatus(packageName) + removeFaultyAppByPackageName(packageName) + } + + Intent.ACTION_PACKAGE_REMOVED -> { + if (!isUpdating) deleteDownload(packageName) + removeFaultyAppByPackageName(packageName) + } + + AppLoungePackageManager.ERROR_PACKAGE_INSTALL -> { + Timber.e("Installation failed due to error: $extra") + updateInstallationIssue(packageName) } } } @@ -103,7 +99,7 @@ open class PkgManagerBR : BroadcastReceiver() { private fun deleteDownload(pkgName: String) { GlobalScope.launch { val fusedDownload = appManagerWrapper.getFusedDownload(packageName = pkgName) - appManagerWrapper.cancelDownload(fusedDownload) + appManagerWrapper.cancelDownload(fusedDownload, pkgName) } } -- GitLab From c352fd40d8f1ca30a2879a6505ad6326bc5f68ec Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 2 Jul 2024 10:56:59 +0600 Subject: [PATCH 17/22] test: fixed test errors --- .../test/java/foundation/e/apps/fusedManager/FakeAppManager.kt | 2 +- .../foundation/e/apps/installProcessor/FakeAppManagerWrapper.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/fusedManager/FakeAppManager.kt b/app/src/test/java/foundation/e/apps/fusedManager/FakeAppManager.kt index f8f1c97d9..20abb9f03 100644 --- a/app/src/test/java/foundation/e/apps/fusedManager/FakeAppManager.kt +++ b/app/src/test/java/foundation/e/apps/fusedManager/FakeAppManager.kt @@ -59,7 +59,7 @@ class FakeAppManager(private val appInstallDAO: AppInstallDAO) : AppManager { TODO("Not yet implemented") } - override suspend fun cancelDownload(appInstall: AppInstall) { + override suspend fun cancelDownload(appInstall: AppInstall, packageName: String) { TODO("Not yet implemented") } diff --git a/app/src/test/java/foundation/e/apps/installProcessor/FakeAppManagerWrapper.kt b/app/src/test/java/foundation/e/apps/installProcessor/FakeAppManagerWrapper.kt index b392cca60..eb026c5ba 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/FakeAppManagerWrapper.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/FakeAppManagerWrapper.kt @@ -103,7 +103,7 @@ class FakeAppManagerWrapper( return installationStatus } - override suspend fun cancelDownload(appInstall: AppInstall) { + override suspend fun cancelDownload(appInstall: AppInstall, packageName: String) { fusedDownloadDAO.deleteDownload(appInstall) } } -- GitLab From b7a86f1b10c3935c52c57b2adafb2395de650506 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 8 Jul 2024 15:40:46 +0530 Subject: [PATCH 18/22] simplify according to gitlab recommendation --- .../data/parentalcontrol/ContentRatingDao.kt | 4 +-- .../fdroid/FDroidAntiFeatureRepository.kt | 30 ++++++++++++------- .../GPlayContentRatingRepository.kt | 23 +++++++++----- .../apps/domain/ValidateAppAgeLimitUseCase.kt | 12 +++++--- 4 files changed, 45 insertions(+), 24 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 76e6fcd2d..109510f93 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 @@ -42,10 +42,10 @@ data class FDroidNsfwApp( interface ContentRatingDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAgeGroups(ageGroups: List) + suspend fun insertContentRatingGroups(ageGroups: List) @Query("SELECT * FROM GPlayContentRatingGroup") - suspend fun getAllAgeGroups(): List + suspend fun getAllContentRatingGroups(): List @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertFDroidNsfwApp(nsfwApps: List) diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt index 164cbdc10..a8bbce78c 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/fdroid/FDroidAntiFeatureRepository.kt @@ -34,18 +34,26 @@ constructor( private set suspend fun fetchNsfwApps() { - val fetchedFDroidNsfwApps = runCatching { - fDroidMonitorApi.getMonitorData().body()?.getNsfwApps() - }.getOrNull() - - fDroidNsfwApps = if (fetchedFDroidNsfwApps != null) { - fetchedFDroidNsfwApps.run { - contentRatingDao.clearFDroidNsfwApps() - contentRatingDao.insertFDroidNsfwApp(this.map { FDroidNsfwApp(it) }) - } - fetchedFDroidNsfwApps + val fetchedFDroidNsfwApps = getFromApi() + + fDroidNsfwApps = if (fetchedFDroidNsfwApps.isEmpty()) { + getFromDb() } else { - contentRatingDao.getAllFDroidNsfwApp().map { it.packageName } + contentRatingDao.clearFDroidNsfwApps() + fetchedFDroidNsfwApps.also { pkgNames -> + contentRatingDao.insertFDroidNsfwApp(pkgNames.map { FDroidNsfwApp(it) }) + } } } + + private suspend fun getFromDb(): List { + return contentRatingDao.getAllFDroidNsfwApp().map { it.packageName } + } + + private suspend fun getFromApi(): List { + return runCatching { + fDroidMonitorApi.getMonitorData().body()?.getNsfwApps() ?: emptyList() + }.getOrElse { emptyList() } + } + } diff --git a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt index bd683b629..a2421d626 100644 --- a/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/parentalcontrol/googleplay/GPlayContentRatingRepository.kt @@ -37,18 +37,27 @@ class GPlayContentRatingRepository @Inject constructor( get() = _contentRatingGroups suspend fun fetchContentRatingData() { - val fetchedAgeGroups = runCatching { - ageGroupApi.getDefinedAgeGroups().body() - }.getOrNull() + val fetchedContentRatingGroups = getFromApi() - _contentRatingGroups = if (fetchedAgeGroups != null) { - contentRatingDao.insertAgeGroups(fetchedAgeGroups) - fetchedAgeGroups + _contentRatingGroups = if (fetchedContentRatingGroups.isEmpty()) { + getFromDb() } else { - contentRatingDao.getAllAgeGroups() + fetchedContentRatingGroups.also { + contentRatingDao.insertContentRatingGroups(it) + } } } + private suspend fun getFromDb(): List { + return contentRatingDao.getAllContentRatingGroups() + } + + private suspend fun getFromApi(): List { + return runCatching { + ageGroupApi.getDefinedAgeGroups().body() ?: emptyList() + }.getOrElse { emptyList() } + } + suspend fun getEnglishContentRating(packageName: String): ContentRating? { return handleNetworkResult { playStoreRepository.getEnglishContentRating(packageName) 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 fad635d10..fd1c68e9c 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -120,15 +120,19 @@ class ValidateAppAgeLimitUseCase @Inject constructor( Timber.d("Fetched content rating - ${app.packageName} - ${fetchedContentRating?.id}") - if (fetchedContentRating != null) { - app.contentRating = fetchedContentRating - } else { + app.contentRating = if (fetchedContentRating == null) { val contentRatingDb = contentRatingDao.getContentRating(app.packageName) Timber.d("Content rating from DB - ${app.packageName} - ${contentRatingDb?.ratingId}") - app.contentRating = ContentRating( + ContentRating ( id = contentRatingDb?.ratingId ?: "", title = contentRatingDb?.ratingTitle ?: "", ) + } else { + fetchedContentRating.also { + contentRatingDao.insertAgeRating( + ContentRatingEntity(app.packageName, it.id, it.title) + ) + } } } -- GitLab From dea4aca5116e9c45ee31a8e514732224b4bca612 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 8 Jul 2024 18:27:10 +0530 Subject: [PATCH 19/22] rename some dao methods --- .../java/foundation/e/apps/data/install/AppManagerImpl.kt | 4 ++-- .../e/apps/data/parentalcontrol/ContentRatingDao.kt | 4 ++-- .../foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt index 114a8b230..1757e7d2f 100644 --- a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt @@ -106,7 +106,7 @@ class AppManagerImpl @Inject constructor( } private suspend fun insertContentRating(appInstall: AppInstall) { - contentRatingDao.insertAgeRating( + contentRatingDao.insertContentRating( ContentRatingEntity( appInstall.packageName, appInstall.contentRating.id, @@ -161,7 +161,7 @@ class AppManagerImpl @Inject constructor( } else { Timber.d("Unable to cancel download!") } - contentRatingDao.deleteAgeRating(packageName) + contentRatingDao.deleteContentRating(packageName) } } 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 109510f93..9568f1e96 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 @@ -57,10 +57,10 @@ interface ContentRatingDao { suspend fun clearFDroidNsfwApps() @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAgeRating(contentRatingEntity: ContentRatingEntity) + suspend fun insertContentRating(contentRatingEntity: ContentRatingEntity) @Query("DELETE FROM ContentRatingEntity WHERE packageName = :packageName") - suspend fun deleteAgeRating(packageName: String) + suspend fun deleteContentRating(packageName: String) @Query("SELECT * FROM ContentRatingEntity WHERE packageName = :packageName LIMIT 1") suspend fun getContentRating(packageName: String): ContentRatingEntity? 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 fd1c68e9c..224e7e218 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -129,8 +129,8 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ) } else { fetchedContentRating.also { - contentRatingDao.insertAgeRating( - ContentRatingEntity(app.packageName, it.id, it.title) + contentRatingDao.insertContentRating( + ContentRatingEntity(app.packageName, it.id.lowercase(), it.title) ) } } -- GitLab From 824d767a09d6e72ae9c45192b7d9172c679d355c Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 8 Jul 2024 18:30:01 +0530 Subject: [PATCH 20/22] add shouldStore variable --- .../e/apps/domain/ValidateAppAgeLimitUseCase.kt | 14 +++++++------- .../e/apps/provider/AgeRatingProvider.kt | 5 ++++- 2 files changed, 11 insertions(+), 8 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 224e7e218..145e918e9 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -46,17 +46,15 @@ class ValidateAppAgeLimitUseCase @Inject constructor( const val KEY_ANTI_FEATURES_NSFW = "NSFW" } - suspend operator fun invoke(app: AppInstall): ResultSupreme { + suspend operator fun invoke(app: AppInstall, shouldStore: Boolean = false): ResultSupreme { val ageGroup = parentalControlRepository.getSelectedAgeGroup() - val hasNoContentRatingOnGPlay = app.origin == Origin.GPLAY - && hasNoContentRatingOnGPlay(app) return when { isParentalControlDisabled(ageGroup) -> ResultSupreme.Success(data = true) isKnownNsfwApp(app) -> ResultSupreme.Success(data = false) isCleanApkApp(app) -> ResultSupreme.Success(!isNsfwAppByCleanApkApi(app)) isWhiteListedCleanApkApp(app) -> ResultSupreme.Success(data = true) - hasNoContentRatingOnGPlay -> ResultSupreme.Error() + hasNoContentRatingOnGPlay(app, shouldStore) -> ResultSupreme.Error() else -> validateAgeLimit(ageGroup, app) } } @@ -99,8 +97,9 @@ class ValidateAppAgeLimitUseCase @Inject constructor( return ResultSupreme.Success(isValidAppAgeRating(app, allowedContentRating)) } - private suspend fun hasNoContentRatingOnGPlay(app: AppInstall) = - !verifyContentRatingExists(app) + private suspend fun hasNoContentRatingOnGPlay(app: AppInstall, shouldStore: Boolean): Boolean { + return app.origin == Origin.GPLAY && !verifyContentRatingExists(app, shouldStore) + } private fun isValidAppAgeRating( app: AppInstall, @@ -112,7 +111,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private fun isParentalControlDisabled(ageGroup: Age) = ageGroup == Age.PARENTAL_CONTROL_DISABLED - private suspend fun verifyContentRatingExists(app: AppInstall): Boolean { + private suspend fun verifyContentRatingExists(app: AppInstall, shouldStore: Boolean): Boolean { if (app.contentRating.id.isEmpty()) { val fetchedContentRating = @@ -129,6 +128,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ) } else { fetchedContentRating.also { + if (!shouldStore) return@also contentRatingDao.insertContentRating( ContentRatingEntity(app.packageName, it.id.lowercase(), it.title) ) 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 86fdf8c15..23f0b8bba 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -203,7 +203,10 @@ class AgeRatingProvider : ContentProvider() { packageName = packageName, origin = Origin.GPLAY ) - val validateResult = validateAppAgeLimitUseCase.invoke(fakeAppInstall) + val validateResult = validateAppAgeLimitUseCase.invoke( + fakeAppInstall, + shouldStore = true, + ) return validateResult.data } -- GitLab From b9490c6af775fd566b9a93cc67e9e93fe462cadb Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 8 Jul 2024 20:26:16 +0530 Subject: [PATCH 21/22] remove test --- .../install/AppInstallConverterTest.kt | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt diff --git a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt b/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt deleted file mode 100644 index 0fbd2ff31..000000000 --- a/app/src/test/java/foundation/e/apps/data/database/install/AppInstallConverterTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.data.database.install - -import com.aurora.gplayapi.data.models.File -import kotlin.test.assertEquals -import org.junit.Before -import org.junit.Test - -class AppInstallConverterTest { - - private lateinit var converter: AppInstallConverter - - @Before - fun setup() { - converter = AppInstallConverter() - } - - @Test - fun `test converting kotlin list to json string`() { - val list = listOf( - "Item 1", - "Item 2", - "Item 3", - ) - val convertedJson = converter.listToJsonString(list) - val expectedJson = "[\"Item 1\",\"Item 2\",\"Item 3\"]" - assertEquals(expectedJson, convertedJson) - } - - @Test - fun `test converting json string to kotlin list`() { - val jsonString = "[\"Item 1\",\"Item 2\",\"Item 3\"]" - val convertedList = converter.jsonStringToList(jsonString) - val expectedList = listOf( - "Item 1", - "Item 2", - "Item 3", - ) - assertEquals(expectedList, convertedList) - } - - @Test - fun `test converting kotlin boolean map to json string`() { - val map = mutableMapOf( - Pair(1L, true), - Pair(2L, false), - Pair(3L, true), - ) - val convertedJson = converter.listToJsonLong(map) - val expectedJson = "{\"1\":true,\"2\":false,\"3\":true}" - assertEquals(expectedJson, convertedJson) - } - - @Test - fun `test converting json string to kotlin boolean map`() { - val jsonString = "{\"1\":true,\"2\":false,\"3\":true}" - val convertedMap = converter.jsonLongToList(jsonString) - val expectedMap = mutableMapOf( - Pair(1L, true), - Pair(2L, false), - Pair(3L, true), - ) - assertEquals(expectedMap, convertedMap) - } - - @Test - fun `test converting File list to json string`() { - val listOfFiles = listOf( - File( - id = "090gfg", - name = "File 1", - url = "example1.com", - size = 1L, - type = File.FileType.BASE, - sha1 = "abc45", - sha256 = "abcd456" - ), - File( - id = "898sas", - name = "File 2", - url = "example2.com", - size = 2L, - type = File.FileType.OBB, - sha1 = "xyz89", - sha256 = "wxyz789" - ) - ) - val convertedString = converter.filesToJsonString(listOfFiles) - val expectedString = "[{\"id\":\"090gfg\",\"name\":\"File 1\",\"url\":\"example1.com\",\"size\":1,\"type\":\"BASE\",\"sha1\":\"abc45\",\"sha256\":\"abcd456\"},{\"id\":\"898sas\",\"name\":\"File 2\",\"url\":\"example2.com\",\"size\":2,\"type\":\"OBB\",\"sha1\":\"xyz89\",\"sha256\":\"wxyz789\"}]" - assertEquals(expectedString, convertedString) - } - - @Test - fun `test converting json string to File list`() { - val jsonString = "[{\"id\":\"090gfg\",\"name\":\"File 1\",\"url\":\"example1.com\",\"size\":1,\"type\":\"BASE\",\"sha1\":\"abc45\",\"sha256\":\"abcd456\"},{\"id\":\"898sas\",\"name\":\"File 2\",\"url\":\"example2.com\",\"size\":2,\"type\":\"OBB\",\"sha1\":\"xyz89\",\"sha256\":\"wxyz789\"}]" - val convertedList = converter.jsonStringToFiles(jsonString) - val expectedList = listOf( - File( - id = "090gfg", - name = "File 1", - url = "example1.com", - size = 1L, - type = File.FileType.BASE, - sha1 = "abc45", - sha256 = "abcd456" - ), - File( - id = "898sas", - name = "File 2", - url = "example2.com", - size = 2L, - type = File.FileType.OBB, - sha1 = "xyz89", - sha256 = "wxyz789" - ) - ) - assertEquals(expectedList, convertedList) - } -} -- GitLab From e2e7bc2a9761bfa3450f1ddc9eddb72c21cee28d Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 9 Jul 2024 21:07:57 +0600 Subject: [PATCH 22/22] refactor: removed shouldStore parameter --- .../apps/domain/ValidateAppAgeLimitUseCase.kt | 52 +++++++++++-------- .../domain/model/ContentRatingValidity.kt | 24 +++++++++ .../workmanager/AppInstallProcessor.kt | 2 +- .../e/apps/provider/AgeRatingProvider.kt | 32 +++++++++--- .../AppInstallProcessorTest.kt | 5 +- 5 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt 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 145e918e9..89c0c51e9 100644 --- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt @@ -27,10 +27,10 @@ import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.Type import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository -import foundation.e.apps.data.parentalcontrol.ContentRatingEntity import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository +import foundation.e.apps.domain.model.ContentRatingValidity import timber.log.Timber import javax.inject.Inject @@ -46,15 +46,24 @@ class ValidateAppAgeLimitUseCase @Inject constructor( const val KEY_ANTI_FEATURES_NSFW = "NSFW" } - suspend operator fun invoke(app: AppInstall, shouldStore: Boolean = false): ResultSupreme { + suspend operator fun invoke(app: AppInstall): ResultSupreme { val ageGroup = parentalControlRepository.getSelectedAgeGroup() return when { - isParentalControlDisabled(ageGroup) -> ResultSupreme.Success(data = true) - isKnownNsfwApp(app) -> ResultSupreme.Success(data = false) - isCleanApkApp(app) -> ResultSupreme.Success(!isNsfwAppByCleanApkApi(app)) - isWhiteListedCleanApkApp(app) -> ResultSupreme.Success(data = true) - hasNoContentRatingOnGPlay(app, shouldStore) -> ResultSupreme.Error() + isParentalControlDisabled(ageGroup) -> ResultSupreme.Success( + data = ContentRatingValidity(true,) + ) + + isKnownNsfwApp(app) -> ResultSupreme.Success(data = ContentRatingValidity(false)) + isCleanApkApp(app) -> ResultSupreme.Success( + ContentRatingValidity(!isNsfwAppByCleanApkApi(app)) + ) + + isWhiteListedCleanApkApp(app) -> ResultSupreme.Success( + data = ContentRatingValidity(true) + ) + + hasNoContentRatingOnGPlay(app) -> ResultSupreme.Error() else -> validateAgeLimit(ageGroup, app) } } @@ -84,7 +93,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private fun validateAgeLimit( ageGroup: Age, app: AppInstall - ): ResultSupreme.Success { + ): ResultSupreme { val allowedContentRating = gPlayContentRatingRepository.contentRatingGroups.find { it.id == ageGroup.toString() } @@ -94,11 +103,18 @@ class ValidateAppAgeLimitUseCase @Inject constructor( "Allowed content rating: $allowedContentRating" ) - return ResultSupreme.Success(isValidAppAgeRating(app, allowedContentRating)) + return ResultSupreme.Success( + ContentRatingValidity( + isValidAppAgeRating( + app, + allowedContentRating + ), app.contentRating + ) + ) } - private suspend fun hasNoContentRatingOnGPlay(app: AppInstall, shouldStore: Boolean): Boolean { - return app.origin == Origin.GPLAY && !verifyContentRatingExists(app, shouldStore) + private suspend fun hasNoContentRatingOnGPlay(app: AppInstall): Boolean { + return app.origin == Origin.GPLAY && !verifyContentRatingExists(app) } private fun isValidAppAgeRating( @@ -111,7 +127,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private fun isParentalControlDisabled(ageGroup: Age) = ageGroup == Age.PARENTAL_CONTROL_DISABLED - private suspend fun verifyContentRatingExists(app: AppInstall, shouldStore: Boolean): Boolean { + private suspend fun verifyContentRatingExists(app: AppInstall): Boolean { if (app.contentRating.id.isEmpty()) { val fetchedContentRating = @@ -122,18 +138,12 @@ class ValidateAppAgeLimitUseCase @Inject constructor( app.contentRating = if (fetchedContentRating == null) { val contentRatingDb = contentRatingDao.getContentRating(app.packageName) Timber.d("Content rating from DB - ${app.packageName} - ${contentRatingDb?.ratingId}") - ContentRating ( + ContentRating( id = contentRatingDb?.ratingId ?: "", title = contentRatingDb?.ratingTitle ?: "", ) - } else { - fetchedContentRating.also { - if (!shouldStore) return@also - contentRatingDao.insertContentRating( - ContentRatingEntity(app.packageName, it.id.lowercase(), it.title) - ) - } - } + } else fetchedContentRating + } return app.contentRating.title.isNotEmpty() && diff --git a/app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt b/app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt new file mode 100644 index 000000000..57ff5b5c5 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt @@ -0,0 +1,24 @@ +/* + * Copyright MURENA SAS 2024 + * Apps Quickly and easily install Android apps onto your device! + * + * 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.domain.model + +import com.aurora.gplayapi.data.models.ContentRating + +data class ContentRatingValidity(val isValid: Boolean, val contentRating: ContentRating? = null) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 77fbf8b44..83d9d7e94 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -132,7 +132,7 @@ class AppInstallProcessor @Inject constructor( } val ageLimitValidationResult = validateAppAgeLimitUseCase.invoke(appInstall) - if (ageLimitValidationResult.data != true) { + if (ageLimitValidationResult.data?.isValid != true) { if (ageLimitValidationResult.isSuccess()) { Timber.i("Content rating is not allowed for: ${appInstall.name}") EventBus.invokeEvent(AppEvent.AgeLimitRestrictionEvent(appInstall.name)) 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 23f0b8bba..f7cda6a02 100644 --- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt +++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt @@ -39,13 +39,17 @@ import foundation.e.apps.contract.ParentalControlContract.COLUMN_PACKAGE_NAME import foundation.e.apps.contract.ParentalControlContract.PATH_BLOCKLIST import foundation.e.apps.contract.ParentalControlContract.PATH_LOGIN_TYPE import foundation.e.apps.contract.ParentalControlContract.getAppLoungeProviderAuthority +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.parentalcontrol.ContentRatingDao +import foundation.e.apps.data.parentalcontrol.ContentRatingEntity import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository 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 kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async @@ -66,6 +70,7 @@ class AgeRatingProvider : ContentProvider() { fun provideValidateAppAgeLimitUseCase(): ValidateAppAgeLimitUseCase fun provideDataStoreManager(): DataStoreManager fun provideNotificationManager(): NotificationManager + fun provideContentRatingDao(): ContentRatingDao } companion object { @@ -80,6 +85,7 @@ class AgeRatingProvider : ContentProvider() { private lateinit var validateAppAgeLimitUseCase: ValidateAppAgeLimitUseCase private lateinit var dataStoreManager: DataStoreManager private lateinit var notificationManager: NotificationManager + private lateinit var contentRatingDao: ContentRatingDao private enum class UriCode(val code: Int) { LoginType(1), @@ -203,11 +209,23 @@ class AgeRatingProvider : ContentProvider() { packageName = packageName, origin = Origin.GPLAY ) - val validateResult = validateAppAgeLimitUseCase.invoke( - fakeAppInstall, - shouldStore = true, - ) - return validateResult.data + val validateResult = validateAppAgeLimitUseCase.invoke(fakeAppInstall) + saveContentRatingIfInvalid(validateResult, packageName) + + return validateResult.data?.isValid + } + + private suspend fun saveContentRatingIfInvalid( + 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) + ) + } } private suspend fun isAppValidRegardingNSWF(packageName: String): Boolean { @@ -216,7 +234,7 @@ class AgeRatingProvider : ContentProvider() { origin = Origin.CLEANAPK, ) val validateResult = validateAppAgeLimitUseCase.invoke(fakeAppInstall) - return validateResult.data ?: false + return validateResult.data?.isValid ?: false } private suspend fun shouldAllow(packageName: String): Boolean { @@ -239,6 +257,7 @@ class AgeRatingProvider : ContentProvider() { }.awaitAll() validityList.forEachIndexed { index: Int, isValid: Boolean? -> if (isValid != true) { + // Collect package names for blocklist cursor.addRow(arrayOf(packageNames[index])) } @@ -259,6 +278,7 @@ class AgeRatingProvider : ContentProvider() { validateAppAgeLimitUseCase = hiltEntryPoint.provideValidateAppAgeLimitUseCase() dataStoreManager = hiltEntryPoint.provideDataStoreManager() notificationManager = hiltEntryPoint.provideNotificationManager() + contentRatingDao = hiltEntryPoint.provideContentRatingDao() return true } diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index 480eeadda..5d893eb60 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -32,6 +32,7 @@ import foundation.e.apps.data.install.AppManager import foundation.e.apps.data.install.models.AppInstall 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.AppInstallComponents import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.install.workmanager.AppInstallProcessor @@ -194,7 +195,7 @@ class AppInstallProcessorTest { fun `processInstallTest when age limit is satisfied`() = runTest { val fusedDownload = initTest() Mockito.`when`(validateAppAgeRatingUseCase.invoke(fusedDownload)) - .thenReturn(ResultSupreme.create(ResultStatus.OK, true)) + .thenReturn(ResultSupreme.create(ResultStatus.OK, ContentRatingValidity(true))) val finalFusedDownload = runProcessInstall(fusedDownload) assertEquals("processInstall", finalFusedDownload, null) @@ -204,7 +205,7 @@ class AppInstallProcessorTest { fun `processInstallTest when age limit is not satisfied`() = runTest { val fusedDownload = initTest() Mockito.`when`(validateAppAgeRatingUseCase.invoke(fusedDownload)) - .thenReturn(ResultSupreme.create(ResultStatus.OK, false)) + .thenReturn(ResultSupreme.create(ResultStatus.OK, ContentRatingValidity(false))) val finalFusedDownload = runProcessInstall(fusedDownload) assertEquals("processInstall", finalFusedDownload, null) -- GitLab