Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fce432fe authored by Nishith  Khanna's avatar Nishith Khanna
Browse files

Merge branch '9999-a14-applounge' into 'main'

Deeper Parental control integration

See merge request !562
parents 39a689c9 906cb4cb
Loading
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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>
@@ -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>
+26 −3
Original line number Diff line number Diff line
@@ -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(
@@ -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 {
@@ -57,3 +74,9 @@ enum class Age {
    SEVENTEEN,
    PARENTAL_CONTROL_DISABLED
}

enum class ProtectionMode {
    REGULAR_MODE,
    REQUEST_PIN_MODE,
    DISABLED_MODE
}
+31 −13
Original line number Diff line number Diff line
@@ -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
@@ -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 {
@@ -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)
        }
    }
@@ -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)
        )
    }

@@ -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
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -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
+22 −6
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -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