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

Verified Commit dfe66028 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor: resolve detekt complaints

parent c7f9990a
Loading
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import foundation.e.apps.domain.model.ContentRatingValidity
import kotlinx.coroutines.CompletableDeferred
import javax.inject.Inject

@Suppress("ReturnCount") // FIXME: Remove suppression and fix detekt
class AppInstallAgeLimitGate @Inject constructor(
    private val validateAppAgeLimitUseCase: ValidateAppAgeLimitUseCase,
    private val appManagerWrapper: AppManagerWrapper,
@@ -38,28 +37,37 @@ class AppInstallAgeLimitGate @Inject constructor(
) {
    suspend fun allow(appInstall: AppInstall): Boolean {
        val ageLimitValidationResult = validateAppAgeLimitUseCase(appInstall)
        if (ageLimitValidationResult.data?.isValid == true) {
            return true
        }
        val isAllowed = when {
            ageLimitValidationResult.data?.isValid == true -> true
            ageLimitValidationResult.isSuccess() -> handleSuccessfulValidation(
                ageLimitValidationResult,
                appInstall.name
            )

        if (ageLimitValidationResult.isSuccess()) {
            awaitInvokeAgeLimitEvent(appInstall.name)
            if (ageLimitValidationResult.data?.requestPin == true) {
                val isAuthenticated = parentalControlAuthGateway.awaitAuthentication()
                if (isAuthenticated) {
                    ageLimitValidationResult.setData(ContentRatingValidity(true))
            else -> {
                appEventDispatcher.dispatch(AppEvent.ErrorMessageDialogEvent(R.string.data_load_error_desc))
                false
            }
        }
        } else {
            appEventDispatcher.dispatch(AppEvent.ErrorMessageDialogEvent(R.string.data_load_error_desc))

        if (!isAllowed) {
            appManagerWrapper.cancelDownload(appInstall)
        }

        if (ageLimitValidationResult.data?.isValid == true) {
            return true
        return isAllowed
    }

        appManagerWrapper.cancelDownload(appInstall)
        return false
    private suspend fun handleSuccessfulValidation(
        ageLimitValidationResult: foundation.e.apps.data.ResultSupreme<ContentRatingValidity>,
        appName: String
    ): Boolean {
        awaitInvokeAgeLimitEvent(appName)
        if (ageLimitValidationResult.data?.requestPin == true &&
            parentalControlAuthGateway.awaitAuthentication()
        ) {
            ageLimitValidationResult.setData(ContentRatingValidity(true))
        }
        return ageLimitValidationResult.data?.isValid == true
    }

    private suspend fun awaitInvokeAgeLimitEvent(type: String) {
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 e Foundation
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.install.workmanager

import foundation.e.apps.R
import foundation.e.apps.data.event.AppEvent
import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.install.notification.StorageNotificationManager
import foundation.e.apps.data.install.wrapper.AppEventDispatcher
import foundation.e.apps.data.install.wrapper.NetworkStatusChecker
import foundation.e.apps.data.install.wrapper.StorageSpaceChecker
import timber.log.Timber
import javax.inject.Inject

class AppInstallDevicePreconditions @Inject constructor(
    private val appManagerWrapper: AppManagerWrapper,
    private val appEventDispatcher: AppEventDispatcher,
    private val storageNotificationManager: StorageNotificationManager,
    private val storageSpaceChecker: StorageSpaceChecker,
    private val networkStatusChecker: NetworkStatusChecker,
) {
    suspend fun canProceed(appInstall: AppInstall): Boolean {
        val hasNetwork = hasNetworkConnection(appInstall)
        return hasNetwork && hasStorageSpace(appInstall)
    }

    private suspend fun hasNetworkConnection(appInstall: AppInstall): Boolean {
        val hasNetwork = networkStatusChecker.isNetworkAvailable()
        if (!hasNetwork) {
            appManagerWrapper.installationIssue(appInstall)
            appEventDispatcher.dispatch(AppEvent.NoInternetEvent(false))
        }
        return hasNetwork
    }

    private suspend fun hasStorageSpace(appInstall: AppInstall): Boolean {
        val missingStorage = storageSpaceChecker.spaceMissing(appInstall)
        if (missingStorage > 0) {
            Timber.d("Storage is not available for: ${appInstall.name} size: ${appInstall.appSize}")
            storageNotificationManager.showNotEnoughSpaceNotification(appInstall)
            appManagerWrapper.installationIssue(appInstall)
            appEventDispatcher.dispatch(AppEvent.ErrorMessageEvent(R.string.not_enough_storage))
        }
        return missingStorage <= 0
    }
}
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 e Foundation
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.install.workmanager

import com.aurora.gplayapi.exceptions.InternalException
import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.application.ApplicationRepository
import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.event.AppEvent
import foundation.e.apps.data.install.AppManager
import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.install.wrapper.AppEventDispatcher
import foundation.e.apps.data.playstore.utils.GplayHttpRequestException
import kotlinx.coroutines.CancellationException
import timber.log.Timber
import javax.inject.Inject

class AppInstallDownloadUrlRefresher @Inject constructor(
    private val applicationRepository: ApplicationRepository,
    private val appManagerWrapper: AppManagerWrapper,
    private val appEventDispatcher: AppEventDispatcher,
    private val appManager: AppManager,
) {
    suspend fun updateDownloadUrls(appInstall: AppInstall): Boolean {
        return runCatching {
            applicationRepository.updateFusedDownloadWithDownloadingInfo(
                appInstall.source,
                appInstall
            )
        }.fold(
            onSuccess = { true },
            onFailure = { throwable -> handleUpdateDownloadFailure(appInstall, throwable) }
        )
    }

    private suspend fun handleUpdateDownloadFailure(appInstall: AppInstall, throwable: Throwable): Boolean {
        return when (throwable) {
            is CancellationException -> throw throwable
            is InternalException.AppNotPurchased -> handleAppNotPurchased(appInstall)
            is GplayHttpRequestException -> {
                handleUpdateDownloadError(
                    appInstall,
                    "${appInstall.packageName} code: ${throwable.status} exception: ${throwable.localizedMessage}",
                    throwable
                )
                false
            }

            is IllegalStateException -> {
                Timber.e(throwable)
                false
            }

            is Exception -> {
                handleUpdateDownloadError(
                    appInstall,
                    "${appInstall.packageName} exception: ${throwable.localizedMessage}",
                    throwable
                )
                false
            }

            else -> throw throwable
        }
    }

    private suspend fun handleAppNotPurchased(appInstall: AppInstall): Boolean {
        if (appInstall.isFree) {
            appEventDispatcher.dispatch(AppEvent.AppRestrictedOrUnavailable(appInstall))
            appManager.addDownload(appInstall)
            appManager.updateUnavailable(appInstall)
        } else {
            appManagerWrapper.addFusedDownloadPurchaseNeeded(appInstall)
            appEventDispatcher.dispatch(AppEvent.AppPurchaseEvent(appInstall))
        }
        return false
    }

    private suspend fun handleUpdateDownloadError(
        appInstall: AppInstall,
        message: String,
        exception: Exception
    ) {
        Timber.e(exception, "Updating download Urls failed for $message")
        appEventDispatcher.dispatch(
            AppEvent.UpdateEvent(
                ResultSupreme.WorkError(
                    ResultStatus.UNKNOWN,
                    appInstall
                )
            )
        )
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 e Foundation
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.install.workmanager

import foundation.e.apps.data.enums.Type
import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.models.AppInstall
import timber.log.Timber
import javax.inject.Inject

class AppInstallPreEnqueueChecker @Inject constructor(
    private val appInstallDownloadUrlRefresher: AppInstallDownloadUrlRefresher,
    private val appManagerWrapper: AppManagerWrapper,
    private val appInstallAgeLimitGate: AppInstallAgeLimitGate,
    private val appInstallDevicePreconditions: AppInstallDevicePreconditions,
) {
    suspend fun canEnqueue(appInstall: AppInstall): Boolean {
        val hasUpdatedDownloadUrls = appInstall.type == Type.PWA ||
            appInstallDownloadUrlRefresher.updateDownloadUrls(appInstall)

        val isDownloadAdded = hasUpdatedDownloadUrls && addDownload(appInstall)
        val isAgeLimitAllowed = isDownloadAdded && appInstallAgeLimitGate.allow(appInstall)

        return isAgeLimitAllowed && appInstallDevicePreconditions.canProceed(appInstall)
    }

    private suspend fun addDownload(appInstall: AppInstall): Boolean {
        val isDownloadAdded = appManagerWrapper.addDownload(appInstall)
        if (!isDownloadAdded) {
            Timber.i("Update adding ABORTED! status")
        }

        return isDownloadAdded
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import foundation.e.apps.data.install.AppInstallComponents
import foundation.e.apps.data.install.models.AppInstall
import javax.inject.Inject

@Suppress("LongParameterList") // FIXME: Remove suppression and fix detekt
class AppInstallProcessor @Inject constructor(
    private val appInstallComponents: AppInstallComponents,
    private val appInstallStartCoordinator: AppInstallStartCoordinator,
Loading