Loading app/detekt-baseline.xml +3 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> <ID>CyclomaticComplexMethod:AppInstallProcessor.kt$AppInstallProcessor$suspend fun enqueueFusedDownload( appInstall: AppInstall, isAnUpdate: Boolean = false, isSystemApp: Boolean = false )</ID> <ID>EmptyCatchBlock:NativeDeviceInfoProviderModule.kt$NativeDeviceInfoProviderModule${ }</ID> <ID>EmptyFunctionBlock:CleanApkAuthenticator.kt$CleanApkAuthenticator${}</ID> <ID>InstanceOfCheckForException:GPlayHttpClient.kt$GPlayHttpClient$e is SocketTimeoutException</ID> Loading Loading @@ -95,6 +96,7 @@ <ID>MaxLineLength:UpdatesWorker.kt$UpdatesWorker$applicationContext.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED</ID> <ID>MayBeConst:EcloudApiInterface.kt$EcloudApiInterface.Companion$val BASE_URL = "https://eu.gtoken.ecloud.global/"</ID> <ID>MemberNameEqualsClassName:Stores.kt$Stores$private val stores = defaultStores.toMutableMap()</ID> <ID>NestedBlockDepth:AppInstallProcessor.kt$AppInstallProcessor$suspend fun enqueueFusedDownload( appInstall: AppInstall, isAnUpdate: Boolean = false, isSystemApp: Boolean = false )</ID> <ID>NewLineAtEndOfFile:ContentRatingValidity.kt$foundation.e.apps.domain.model.ContentRatingValidity.kt</ID> <ID>NewLineAtEndOfFile:HomeConverter.kt$foundation.e.apps.data.cleanapk.repositories.HomeConverter.kt</ID> <ID>NewLineAtEndOfFile:Stores.kt$foundation.e.apps.data.Stores.kt</ID> Loading app/src/main/java/foundation/e/apps/data/parentalcontrol/ParentalControlRepository.kt +26 −3 Original line number Diff line number Diff line Loading @@ -19,10 +19,10 @@ package foundation.e.apps.data.parentalcontrol import android.content.Context import android.net.Uri import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import javax.inject.Singleton import androidx.core.net.toUri @Singleton class ParentalControlRepository @Inject constructor( Loading @@ -32,21 +32,38 @@ class ParentalControlRepository @Inject constructor( companion object { private const val URI_PARENTAL_CONTROL_PROVIDER = "content://foundation.e.parentalcontrol.provider/age" private const val URI_PARENTAL_CONTROL_INSTALL_TYPE_APP_MANAGEMENT = "content://foundation.e.parentalcontrol.provider/protectionmode" const val KEY_PARENTAL_GUIDANCE = "parental guidance" } fun getSelectedAgeGroup(): Age { val uri = Uri.parse(URI_PARENTAL_CONTROL_PROVIDER) val uri = URI_PARENTAL_CONTROL_PROVIDER.toUri() val cursor = context.contentResolver.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { val ageOrdinal = it.getInt(it.getColumnIndexOrThrow("age")) return Age.values()[ageOrdinal] return Age.entries[ageOrdinal] } } return Age.PARENTAL_CONTROL_DISABLED } fun getSelectedProtectionMode(): ProtectionMode { val uri = URI_PARENTAL_CONTROL_INSTALL_TYPE_APP_MANAGEMENT.toUri() val cursor = context.contentResolver.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { val protectionMode = it.getInt(it.getColumnIndexOrThrow("protectionmode")) return ProtectionMode.entries[protectionMode] } } return ProtectionMode.DISABLED_MODE } } enum class Age { Loading @@ -57,3 +74,9 @@ enum class Age { SEVENTEEN, PARENTAL_CONTROL_DISABLED } enum class ProtectionMode { REGULAR_MODE, REQUEST_PIN_MODE, DISABLED_MODE } app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +31 −13 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.Age import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.ParentalControlRepository import foundation.e.apps.data.parentalcontrol.ParentalControlRepository.Companion.KEY_PARENTAL_GUIDANCE import foundation.e.apps.data.parentalcontrol.ProtectionMode import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository Loading @@ -51,6 +53,9 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private val ageGroup: Age get() = parentalControlRepository.getSelectedAgeGroup() private val protectionMode: ProtectionMode get() = parentalControlRepository.getSelectedProtectionMode() suspend operator fun invoke(app: AppInstall): ResultSupreme<ContentRatingValidity> { return when { Loading @@ -75,6 +80,11 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ) hasNoContentRatingOnGPlay(app) -> ResultSupreme.Error() isParentalGuidance(app) -> ResultSupreme.Success( data = ContentRatingValidity(false, requestPin = true) ) else -> validateAgeLimit(ageGroup, app) } } Loading Loading @@ -113,22 +123,21 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ageGroup: Age, app: AppInstall ): ResultSupreme<ContentRatingValidity> { if (protectionMode == ProtectionMode.REGULAR_MODE) { val allowedContentRating = gPlayContentRatingRepository.contentRatingGroups.find { it.id == ageGroup.toString() } Timber.d( "${app.packageName} - Content rating: ${app.contentRating.id} \n" + "Selected age group: $ageGroup \n" + "Allowed content rating: $allowedContentRating" ) "Selected age group: $ageGroup \nAllowed content rating: $allowedContentRating") val isValid = isValidAppAgeRating(app, allowedContentRating) return ResultSupreme.Success( ContentRatingValidity( isValidAppAgeRating( app, allowedContentRating ), app.contentRating ContentRatingValidity(isValid, app.contentRating, requestPin = false) ) } return ResultSupreme.Success( data = ContentRatingValidity(false, requestPin = true) ) } Loading Loading @@ -170,4 +179,13 @@ class ValidateAppAgeLimitUseCase @Inject constructor( return app.contentRating.title.isNotEmpty() && app.contentRating.id.isNotEmpty() } private suspend fun isParentalGuidance(app: AppInstall): Boolean { if (protectionMode == ProtectionMode.REGULAR_MODE) { val fetchedContentRating = gPlayContentRatingRepository.getEnglishContentRating(app.packageName) return fetchedContentRating?.id == KEY_PARENTAL_GUIDANCE } return false } } app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt +2 −1 Original line number Diff line number Diff line Loading @@ -21,4 +21,5 @@ 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 data class ContentRatingValidity(val isValid: Boolean, val contentRating: ContentRating? = null, val requestPin: Boolean = false) No newline at end of file app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +22 −6 Original line number Diff line number Diff line Loading @@ -36,15 +36,18 @@ import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.domain.ValidateAppAgeLimitUseCase import foundation.e.apps.domain.model.ContentRatingValidity import foundation.e.apps.install.AppInstallComponents import foundation.e.apps.install.download.DownloadManagerUtils import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.utils.ParentalControlAuthenticator import foundation.e.apps.utils.StorageComputer import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import foundation.e.apps.utils.getFormattedString import foundation.e.apps.utils.isNetworkAvailable import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.transformWhile import timber.log.Timber Loading Loading @@ -139,15 +142,22 @@ class AppInstallProcessor @Inject constructor( val ageLimitValidationResult = validateAppAgeLimitUseCase.invoke(appInstall) if (ageLimitValidationResult.data?.isValid != true) { if (ageLimitValidationResult.isSuccess()) { Timber.i("Content rating is not allowed for: ${appInstall.name}") EventBus.invokeEvent(AppEvent.AgeLimitRestrictionEvent(appInstall.name)) awaitInvokeAgeLimitEvent(appInstall.name) if (ageLimitValidationResult.data?.requestPin == true){ val isAuthenticated = ParentalControlAuthenticator.awaitAuthentication() if (isAuthenticated) { ageLimitValidationResult.setData(ContentRatingValidity(true)) } } } else { EventBus.invokeEvent(AppEvent.ErrorMessageDialogEvent(R.string.data_load_error_desc)) } if (ageLimitValidationResult.data?.isValid != true) { appInstallComponents.appManagerWrapper.cancelDownload(appInstall) return } } if (!context.isNetworkAvailable()) { appInstallComponents.appManagerWrapper.installationIssue(appInstall) Loading @@ -167,13 +177,19 @@ class AppInstallProcessor @Inject constructor( InstallWorkManager.enqueueWork(appInstall, isAnUpdate) } catch (e: Exception) { Timber.e( "Enqueuing App install work is failed for ${appInstall.packageName} exception: ${e.localizedMessage}", e e, "Enqueuing App install work is failed for ${appInstall.packageName} exception: ${e.localizedMessage}" ) appInstallComponents.appManagerWrapper.installationIssue(appInstall) } } suspend fun awaitInvokeAgeLimitEvent(type: String) { val deferred = CompletableDeferred<Unit>() EventBus.invokeEvent(AppEvent.AgeLimitRestrictionEvent(type, deferred)) deferred.await() // await closing dialog box } // returns TRUE if updating urls is successful, otherwise false. private suspend fun updateDownloadUrls(appInstall: AppInstall): Boolean { try { Loading Loading
app/detekt-baseline.xml +3 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ <SmellBaseline> <ManuallySuppressedIssues></ManuallySuppressedIssues> <CurrentIssues> <ID>CyclomaticComplexMethod:AppInstallProcessor.kt$AppInstallProcessor$suspend fun enqueueFusedDownload( appInstall: AppInstall, isAnUpdate: Boolean = false, isSystemApp: Boolean = false )</ID> <ID>EmptyCatchBlock:NativeDeviceInfoProviderModule.kt$NativeDeviceInfoProviderModule${ }</ID> <ID>EmptyFunctionBlock:CleanApkAuthenticator.kt$CleanApkAuthenticator${}</ID> <ID>InstanceOfCheckForException:GPlayHttpClient.kt$GPlayHttpClient$e is SocketTimeoutException</ID> Loading Loading @@ -95,6 +96,7 @@ <ID>MaxLineLength:UpdatesWorker.kt$UpdatesWorker$applicationContext.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED</ID> <ID>MayBeConst:EcloudApiInterface.kt$EcloudApiInterface.Companion$val BASE_URL = "https://eu.gtoken.ecloud.global/"</ID> <ID>MemberNameEqualsClassName:Stores.kt$Stores$private val stores = defaultStores.toMutableMap()</ID> <ID>NestedBlockDepth:AppInstallProcessor.kt$AppInstallProcessor$suspend fun enqueueFusedDownload( appInstall: AppInstall, isAnUpdate: Boolean = false, isSystemApp: Boolean = false )</ID> <ID>NewLineAtEndOfFile:ContentRatingValidity.kt$foundation.e.apps.domain.model.ContentRatingValidity.kt</ID> <ID>NewLineAtEndOfFile:HomeConverter.kt$foundation.e.apps.data.cleanapk.repositories.HomeConverter.kt</ID> <ID>NewLineAtEndOfFile:Stores.kt$foundation.e.apps.data.Stores.kt</ID> Loading
app/src/main/java/foundation/e/apps/data/parentalcontrol/ParentalControlRepository.kt +26 −3 Original line number Diff line number Diff line Loading @@ -19,10 +19,10 @@ package foundation.e.apps.data.parentalcontrol import android.content.Context import android.net.Uri import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import javax.inject.Singleton import androidx.core.net.toUri @Singleton class ParentalControlRepository @Inject constructor( Loading @@ -32,21 +32,38 @@ class ParentalControlRepository @Inject constructor( companion object { private const val URI_PARENTAL_CONTROL_PROVIDER = "content://foundation.e.parentalcontrol.provider/age" private const val URI_PARENTAL_CONTROL_INSTALL_TYPE_APP_MANAGEMENT = "content://foundation.e.parentalcontrol.provider/protectionmode" const val KEY_PARENTAL_GUIDANCE = "parental guidance" } fun getSelectedAgeGroup(): Age { val uri = Uri.parse(URI_PARENTAL_CONTROL_PROVIDER) val uri = URI_PARENTAL_CONTROL_PROVIDER.toUri() val cursor = context.contentResolver.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { val ageOrdinal = it.getInt(it.getColumnIndexOrThrow("age")) return Age.values()[ageOrdinal] return Age.entries[ageOrdinal] } } return Age.PARENTAL_CONTROL_DISABLED } fun getSelectedProtectionMode(): ProtectionMode { val uri = URI_PARENTAL_CONTROL_INSTALL_TYPE_APP_MANAGEMENT.toUri() val cursor = context.contentResolver.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { val protectionMode = it.getInt(it.getColumnIndexOrThrow("protectionmode")) return ProtectionMode.entries[protectionMode] } } return ProtectionMode.DISABLED_MODE } } enum class Age { Loading @@ -57,3 +74,9 @@ enum class Age { SEVENTEEN, PARENTAL_CONTROL_DISABLED } enum class ProtectionMode { REGULAR_MODE, REQUEST_PIN_MODE, DISABLED_MODE }
app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt +31 −13 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.Age import foundation.e.apps.data.parentalcontrol.ContentRatingDao import foundation.e.apps.data.parentalcontrol.ParentalControlRepository import foundation.e.apps.data.parentalcontrol.ParentalControlRepository.Companion.KEY_PARENTAL_GUIDANCE import foundation.e.apps.data.parentalcontrol.ProtectionMode import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository Loading @@ -51,6 +53,9 @@ class ValidateAppAgeLimitUseCase @Inject constructor( private val ageGroup: Age get() = parentalControlRepository.getSelectedAgeGroup() private val protectionMode: ProtectionMode get() = parentalControlRepository.getSelectedProtectionMode() suspend operator fun invoke(app: AppInstall): ResultSupreme<ContentRatingValidity> { return when { Loading @@ -75,6 +80,11 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ) hasNoContentRatingOnGPlay(app) -> ResultSupreme.Error() isParentalGuidance(app) -> ResultSupreme.Success( data = ContentRatingValidity(false, requestPin = true) ) else -> validateAgeLimit(ageGroup, app) } } Loading Loading @@ -113,22 +123,21 @@ class ValidateAppAgeLimitUseCase @Inject constructor( ageGroup: Age, app: AppInstall ): ResultSupreme<ContentRatingValidity> { if (protectionMode == ProtectionMode.REGULAR_MODE) { val allowedContentRating = gPlayContentRatingRepository.contentRatingGroups.find { it.id == ageGroup.toString() } Timber.d( "${app.packageName} - Content rating: ${app.contentRating.id} \n" + "Selected age group: $ageGroup \n" + "Allowed content rating: $allowedContentRating" ) "Selected age group: $ageGroup \nAllowed content rating: $allowedContentRating") val isValid = isValidAppAgeRating(app, allowedContentRating) return ResultSupreme.Success( ContentRatingValidity( isValidAppAgeRating( app, allowedContentRating ), app.contentRating ContentRatingValidity(isValid, app.contentRating, requestPin = false) ) } return ResultSupreme.Success( data = ContentRatingValidity(false, requestPin = true) ) } Loading Loading @@ -170,4 +179,13 @@ class ValidateAppAgeLimitUseCase @Inject constructor( return app.contentRating.title.isNotEmpty() && app.contentRating.id.isNotEmpty() } private suspend fun isParentalGuidance(app: AppInstall): Boolean { if (protectionMode == ProtectionMode.REGULAR_MODE) { val fetchedContentRating = gPlayContentRatingRepository.getEnglishContentRating(app.packageName) return fetchedContentRating?.id == KEY_PARENTAL_GUIDANCE } return false } }
app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt +2 −1 Original line number Diff line number Diff line Loading @@ -21,4 +21,5 @@ 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 data class ContentRatingValidity(val isValid: Boolean, val contentRating: ContentRating? = null, val requestPin: Boolean = false) No newline at end of file
app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +22 −6 Original line number Diff line number Diff line Loading @@ -36,15 +36,18 @@ import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.domain.ValidateAppAgeLimitUseCase import foundation.e.apps.domain.model.ContentRatingValidity import foundation.e.apps.install.AppInstallComponents import foundation.e.apps.install.download.DownloadManagerUtils import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.utils.ParentalControlAuthenticator import foundation.e.apps.utils.StorageComputer import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import foundation.e.apps.utils.getFormattedString import foundation.e.apps.utils.isNetworkAvailable import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.transformWhile import timber.log.Timber Loading Loading @@ -139,15 +142,22 @@ class AppInstallProcessor @Inject constructor( val ageLimitValidationResult = validateAppAgeLimitUseCase.invoke(appInstall) if (ageLimitValidationResult.data?.isValid != true) { if (ageLimitValidationResult.isSuccess()) { Timber.i("Content rating is not allowed for: ${appInstall.name}") EventBus.invokeEvent(AppEvent.AgeLimitRestrictionEvent(appInstall.name)) awaitInvokeAgeLimitEvent(appInstall.name) if (ageLimitValidationResult.data?.requestPin == true){ val isAuthenticated = ParentalControlAuthenticator.awaitAuthentication() if (isAuthenticated) { ageLimitValidationResult.setData(ContentRatingValidity(true)) } } } else { EventBus.invokeEvent(AppEvent.ErrorMessageDialogEvent(R.string.data_load_error_desc)) } if (ageLimitValidationResult.data?.isValid != true) { appInstallComponents.appManagerWrapper.cancelDownload(appInstall) return } } if (!context.isNetworkAvailable()) { appInstallComponents.appManagerWrapper.installationIssue(appInstall) Loading @@ -167,13 +177,19 @@ class AppInstallProcessor @Inject constructor( InstallWorkManager.enqueueWork(appInstall, isAnUpdate) } catch (e: Exception) { Timber.e( "Enqueuing App install work is failed for ${appInstall.packageName} exception: ${e.localizedMessage}", e e, "Enqueuing App install work is failed for ${appInstall.packageName} exception: ${e.localizedMessage}" ) appInstallComponents.appManagerWrapper.installationIssue(appInstall) } } suspend fun awaitInvokeAgeLimitEvent(type: String) { val deferred = CompletableDeferred<Unit>() EventBus.invokeEvent(AppEvent.AgeLimitRestrictionEvent(type, deferred)) deferred.await() // await closing dialog box } // returns TRUE if updating urls is successful, otherwise false. private suspend fun updateDownloadUrls(appInstall: AppInstall): Boolean { try { Loading