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

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

refactor(install): introduce data-owned ports for installation processing

parent 9110eb13
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.core.helper.InstallationCompletionHandler
import foundation.e.apps.data.install.download.DownloadManagerUtils
import foundation.e.apps.data.install.wrapper.AppEventDispatcher
import foundation.e.apps.data.install.wrapper.DefaultAppEventDispatcher
import foundation.e.apps.data.install.wrapper.DeviceNetworkStatusChecker
@@ -34,6 +37,9 @@ import foundation.e.apps.data.install.wrapper.UpdatesNotificationSender
import foundation.e.apps.data.install.wrapper.UpdatesNotificationSenderImpl
import foundation.e.apps.data.install.wrapper.UpdatesTracker
import foundation.e.apps.data.install.wrapper.UpdatesTrackerImpl
import foundation.e.apps.data.installation.port.InstallationAppManager
import foundation.e.apps.data.installation.port.InstallationCompletionNotifier
import foundation.e.apps.data.installation.port.InstallationDownloadStatusUpdater
import javax.inject.Singleton

@Module
@@ -63,4 +69,18 @@ interface AppInstallationModule {
    @Binds
    @Singleton
    fun bindNetworkStatusChecker(checker: DeviceNetworkStatusChecker): NetworkStatusChecker

    @Binds
    @Singleton
    fun bindInstallationAppManager(appManagerWrapper: AppManagerWrapper): InstallationAppManager

    @Binds
    @Singleton
    fun bindInstallationDownloadStatusUpdater(
        downloadManagerUtils: DownloadManagerUtils
    ): InstallationDownloadStatusUpdater

    @Binds
    @Singleton
    fun bindInstallationCompletionNotifier(handler: InstallationCompletionHandler): InstallationCompletionNotifier
}
+12 −6
Original line number Diff line number Diff line
@@ -27,25 +27,27 @@ import foundation.e.apps.data.fdroid.FDroidRepository
import foundation.e.apps.data.install.download.data.DownloadProgress
import foundation.e.apps.data.install.workmanager.InstallWorkManager
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.port.InstallationAppManager
import foundation.e.apps.domain.model.install.Status
import javax.inject.Inject
import javax.inject.Singleton

private const val PERCENTAGE_MULTIPLIER = 100

@Suppress("TooManyFunctions")
@Singleton
@OpenForTesting
class AppManagerWrapper @Inject constructor(
    @ApplicationContext private val context: Context,
    private val appManager: AppManager,
    private val fDroidRepository: FDroidRepository
) {
) : InstallationAppManager {

    fun createNotificationChannels() {
        return appManager.createNotificationChannels()
    }

    suspend fun downloadApp(appInstall: AppInstall, isUpdate: Boolean = false) {
    override suspend fun downloadApp(appInstall: AppInstall, isUpdate: Boolean) {
        return appManager.downloadApp(appInstall, isUpdate)
    }

@@ -100,15 +102,19 @@ class AppManagerWrapper @Inject constructor(
        return appManager.getFusedDownload(downloadId, packageName)
    }

    suspend fun updateDownloadStatus(appInstall: AppInstall, status: Status) {
    override suspend fun updateDownloadStatus(appInstall: AppInstall, status: Status) {
        return appManager.updateDownloadStatus(appInstall, status)
    }

    override 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) {
    override suspend fun installationIssue(appInstall: AppInstall) {
        return appManager.reportInstallationIssue(appInstall)
    }

@@ -124,7 +130,7 @@ class AppManagerWrapper @Inject constructor(
        appManager.updateAppInstall(appInstall)
    }

    fun validateFusedDownload(appInstall: AppInstall) =
    override fun validateFusedDownload(appInstall: AppInstall) =
        appInstall.packageName.isNotEmpty() && appInstall.downloadURLList.isNotEmpty()

    suspend fun calculateProgress(
@@ -212,7 +218,7 @@ class AppManagerWrapper @Inject constructor(
        )
    }

    fun isFusedDownloadInstalled(appInstall: AppInstall): Boolean {
    override fun isFusedDownloadInstalled(appInstall: AppInstall): Boolean {
        return appManager.isAppInstalled(appInstall)
    }

+18 −17
Original line number Diff line number Diff line
@@ -19,10 +19,10 @@
package foundation.e.apps.data.install.core

import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.core.helper.InstallationCompletionHandler
import foundation.e.apps.data.install.download.DownloadManagerUtils
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.port.InstallationAppManager
import foundation.e.apps.data.installation.port.InstallationCompletionNotifier
import foundation.e.apps.data.installation.port.InstallationDownloadStatusUpdater
import foundation.e.apps.data.installation.repository.AppInstallRepository
import foundation.e.apps.domain.model.install.Status
import kotlinx.coroutines.CancellationException
@@ -33,9 +33,9 @@ import javax.inject.Inject

class InstallationProcessor @Inject constructor(
    private val appInstallRepository: AppInstallRepository,
    private val appManagerWrapper: AppManagerWrapper,
    private val downloadManager: DownloadManagerUtils,
    private val installationCompletionHandler: InstallationCompletionHandler,
    private val installationAppManager: InstallationAppManager,
    private val installationDownloadStatusUpdater: InstallationDownloadStatusUpdater,
    private val installationCompletionNotifier: InstallationCompletionNotifier,
) {
    @Suppress("ReturnCount")
    @OptIn(DelicateCoroutinesApi::class)
@@ -60,8 +60,8 @@ class InstallationProcessor @Inject constructor(
            return Result.failure(IllegalStateException(message))
        }

        if (!appManagerWrapper.validateFusedDownload(appInstall)) {
            appManagerWrapper.installationIssue(appInstall)
        if (!installationAppManager.validateFusedDownload(appInstall)) {
            installationAppManager.installationIssue(appInstall)
            val message = "Installation issue for ${appInstall.name}/${appInstall.packageName}"
            Timber.w(message)

@@ -70,11 +70,11 @@ class InstallationProcessor @Inject constructor(

        return runCatching {
            val isUpdateWork =
                isItUpdateWork && appManagerWrapper.isFusedDownloadInstalled(appInstall)
                isItUpdateWork && installationAppManager.isFusedDownloadInstalled(appInstall)

            if (areFilesDownloadedButNotInstalled(appInstall)) {
                Timber.i("===> Downloaded But not installed ${appInstall.name}")
                appManagerWrapper.updateDownloadStatus(appInstall, Status.INSTALLING)
                installationAppManager.updateDownloadStatus(appInstall, Status.INSTALLING)
            }

            runInForeground.invoke(appInstall.name)
@@ -92,7 +92,8 @@ class InstallationProcessor @Inject constructor(
                "Install worker failed for ${appInstall.packageName} exception: ${exception.message}"
            )

            appManagerWrapper.cancelDownload(appInstall)
            installationAppManager.installationIssue(appInstall)
            installationAppManager.cancelDownload(appInstall)
        }
    }

@@ -100,17 +101,17 @@ class InstallationProcessor @Inject constructor(
    private fun checkDownloadingState(appInstall: AppInstall) {
        if (appInstall.status == Status.DOWNLOADING) {
            appInstall.downloadIdMap.keys.forEach { downloadId ->
                downloadManager.updateDownloadStatus(downloadId)
                installationDownloadStatusUpdater.updateDownloadStatus(downloadId)
            }
        }
    }

    private fun areFilesDownloadedButNotInstalled(appInstall: AppInstall): Boolean = appInstall.areFilesDownloaded() &&
        (!appManagerWrapper.isFusedDownloadInstalled(appInstall) || appInstall.status == Status.INSTALLING)
        (!installationAppManager.isFusedDownloadInstalled(appInstall) || appInstall.status == Status.INSTALLING)

    private suspend fun startAppInstallationProcess(appInstall: AppInstall, isUpdateWork: Boolean) {
        if (appInstall.isAwaiting()) {
            appManagerWrapper.downloadApp(appInstall, isUpdateWork)
            installationAppManager.downloadApp(appInstall, isUpdateWork)
            Timber.i("===> doWork: Download started ${appInstall.name} ${appInstall.status}")
        }

@@ -155,7 +156,7 @@ class InstallationProcessor @Inject constructor(
                        "Handling install status is failed for ${download.packageName} " +
                            "exception: ${throwable.localizedMessage}"
                    Timber.e(throwable, message)
                    appManagerWrapper.installationIssue(download)
                    installationAppManager.installationIssue(download)
                    finishInstallation(download, isUpdateWork)
                }

@@ -167,7 +168,7 @@ class InstallationProcessor @Inject constructor(
    private suspend fun handleFusedDownloadStatus(appInstall: AppInstall, isUpdateWork: Boolean) {
        when (appInstall.status) {
            Status.AWAITING, Status.DOWNLOADING -> Unit
            Status.DOWNLOADED -> appManagerWrapper.updateDownloadStatus(
            Status.DOWNLOADED -> installationAppManager.updateDownloadStatus(
                appInstall,
                Status.INSTALLING
            )
@@ -186,6 +187,6 @@ class InstallationProcessor @Inject constructor(
    }

    private suspend fun finishInstallation(appInstall: AppInstall, isUpdateWork: Boolean) {
        installationCompletionHandler.onInstallFinished(appInstall, isUpdateWork)
        installationCompletionNotifier.onInstallFinished(appInstall, isUpdateWork)
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.wrapper.UpdatesNotificationSender
import foundation.e.apps.data.install.wrapper.UpdatesTracker
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.port.InstallationCompletionNotifier
import foundation.e.apps.data.installation.repository.AppInstallRepository
import foundation.e.apps.data.preference.PlayStoreAuthStore
import foundation.e.apps.data.utils.getFormattedString
@@ -40,12 +41,12 @@ class InstallationCompletionHandler @Inject constructor(
    private val playStoreAuthStore: PlayStoreAuthStore,
    private val updatesTracker: UpdatesTracker,
    private val updatesNotificationSender: UpdatesNotificationSender,
) {
) : InstallationCompletionNotifier {
    companion object {
        private const val DATE_FORMAT = "dd/MM/yyyy-HH:mm"
    }

    suspend fun onInstallFinished(appInstall: AppInstall?, isUpdateWork: Boolean) {
    override suspend fun onInstallFinished(appInstall: AppInstall?, isUpdateWork: Boolean) {
        if (!isUpdateWork) {
            return
        }
+3 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.notification.StorageNotificationManager
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.model.InstallationSource
import foundation.e.apps.data.installation.port.InstallationDownloadStatusUpdater
import foundation.e.apps.domain.model.install.Status
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
@@ -48,7 +49,7 @@ class DownloadManagerUtils @Inject constructor(
    private val downloadManager: DownloadManager,
    private val storageNotificationManager: StorageNotificationManager,
    @IoCoroutineScope private val coroutineScope: CoroutineScope
) {
) : InstallationDownloadStatusUpdater {
    private val mutex = Mutex()

    @DelicateCoroutinesApi
@@ -60,7 +61,7 @@ class DownloadManagerUtils @Inject constructor(
    }

    @DelicateCoroutinesApi
    fun updateDownloadStatus(downloadId: Long) {
    override fun updateDownloadStatus(downloadId: Long) {
        coroutineScope.launch {
            mutex.withLock {
                // Waiting for DownloadManager to publish the progress of last bytes
Loading