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

Commit e3cddddf authored by Hasib Prince's avatar Hasib Prince
Browse files

Merge branch '6810-auto_update' into 'release-1.10-rc'

6810 auto update

See merge request !290
parents 28524bca 3089bf8d
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ class DatabaseRepository @Inject constructor(
        }
    }

    fun getDownloadFlowById(id: String): Flow<FusedDownload> {
    fun getDownloadFlowById(id: String): Flow<FusedDownload?> {
        return fusedDownloadDAO.getDownloadFlowById(id).asFlow()
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.annotation.UiThread
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.DelicateCoroutinesApi
import timber.log.Timber
@@ -36,9 +37,9 @@ class DownloadManagerBR : BroadcastReceiver() {

    companion object {
        private const val TAG = "DownloadManagerBR"
        val downloadedList = mutableListOf<Long>()
    }

    @UiThread
    override fun onReceive(context: Context?, intent: Intent?) {
        val action = intent?.action
        if (context != null && action != null) {
@@ -46,7 +47,6 @@ class DownloadManagerBR : BroadcastReceiver() {
            Timber.i("onReceive: DownloadBR $action $id")
            when (action) {
                DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
                    downloadedList.add(id)
                    downloadManagerUtils.updateDownloadStatus(id)
                }
                DownloadManager.ACTION_NOTIFICATION_CLICKED -> {
+19 −16
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.download.DownloadManagerBR
import foundation.e.apps.manager.download.data.DownloadProgressLD
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.manager.workmanager.InstallWorkManager
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.Type
import foundation.e.apps.utils.modules.PWAManagerModule
@@ -61,6 +62,8 @@ class FusedManagerImpl @Inject constructor(

    private val TAG = FusedManagerImpl::class.java.simpleName

    private val mutex = Mutex()

    @RequiresApi(Build.VERSION_CODES.O)
    override fun createNotificationChannels() {
        notificationManager.apply {
@@ -95,7 +98,6 @@ class FusedManagerImpl @Inject constructor(
    override suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) {
        if (status == Status.INSTALLED) {
            fusedDownload.status = status
            DownloadManagerBR.downloadedList.clear()
            flushOldDownload(fusedDownload.packageName)
            databaseRepository.deleteDownload(fusedDownload)
        } else if (status == Status.INSTALLING) {
@@ -106,8 +108,6 @@ class FusedManagerImpl @Inject constructor(
        }
    }

    private val mutex = Mutex()

    override suspend fun downloadApp(fusedDownload: FusedDownload) {
        mutex.withLock {
            when (fusedDownload.type) {
@@ -147,23 +147,26 @@ class FusedManagerImpl @Inject constructor(

    @OptIn(DelicateCoroutinesApi::class)
    override suspend fun cancelDownload(fusedDownload: FusedDownload) {
        mutex.withLock {
            if (fusedDownload.id.isNotBlank()) {
                removeFusedDownload(fusedDownload)
            } else {
                Timber.d("Unable to cancel download!")
            }
        }
    }

    private suspend fun removeFusedDownload(fusedDownload: FusedDownload) {
        fusedDownload.downloadIdMap.forEach { (key, _) ->
            downloadManager.remove(key)
        }
        DownloadProgressLD.setDownloadId(-1)
            DownloadManagerBR.downloadedList.clear()

            // Reset the status before deleting download
            updateDownloadStatus(fusedDownload, fusedDownload.orgStatus)
        if (fusedDownload.status != Status.INSTALLATION_ISSUE) {
            databaseRepository.deleteDownload(fusedDownload)
        }

        flushOldDownload(fusedDownload.packageName)
        } else {
            Timber.d("Unable to cancel download!")
        }
    }

    override suspend fun getFusedDownload(downloadId: Long, packageName: String): FusedDownload {
+10 −2
Original line number Diff line number Diff line
@@ -37,11 +37,11 @@ class FusedManagerRepository @Inject constructor(
    }

    suspend fun addDownload(fusedDownload: FusedDownload): Boolean {
        if (InstallWorkManager.checkWorkIsAlreadyAvailable(fusedDownload.id)) {
        val existingFusedDownload = fusedManagerImpl.getDownloadById(fusedDownload)
        if (isInstallWorkRunning(existingFusedDownload, fusedDownload)) {
            return false
        }

        val existingFusedDownload = fusedManagerImpl.getDownloadById(fusedDownload)
        // We don't want to add any thing, if it already exists without INSTALLATION_ISSUE
        if (existingFusedDownload != null && existingFusedDownload.status != Status.INSTALLATION_ISSUE) {
            return false
@@ -51,6 +51,14 @@ class FusedManagerRepository @Inject constructor(
        return true
    }

    private fun isInstallWorkRunning(
        existingFusedDownload: FusedDownload?,
        fusedDownload: FusedDownload
    ) =
        existingFusedDownload != null && InstallWorkManager.checkWorkIsAlreadyAvailable(
            fusedDownload.id
        )

    suspend fun addFusedDownloadPurchaseNeeded(fusedDownload: FusedDownload) {
        fusedManagerImpl.insertFusedDownloadPurchaseNeeded(fusedDownload)
    }
+39 −60
Original line number Diff line number Diff line
@@ -29,20 +29,12 @@ import foundation.e.apps.updates.UpdatesNotifier
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.modules.DataStoreManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.flow.transformWhile
import timber.log.Timber
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Date
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

class AppInstallProcessor @Inject constructor(
    @ApplicationContext private val context: Context,
@@ -51,15 +43,12 @@ class AppInstallProcessor @Inject constructor(
    private val dataStoreManager: DataStoreManager
) {

    private var isDownloading: Boolean = false
    private var isItUpdateWork = false

    companion object {
        private const val TAG = "AppInstallProcessor"
    }

    private val mutex = Mutex(true)

    suspend fun processInstall(
        fusedDownloadId: String,
        isItUpdateWork: Boolean,
@@ -96,7 +85,6 @@ class AppInstallProcessor @Inject constructor(
                runInForeground?.invoke(it.name)

                startAppInstallationProcess(it)
                mutex.lock()
            }
        } catch (e: Exception) {
            Timber.e("doWork: Failed: ${e.stackTraceToString()}")
@@ -121,7 +109,8 @@ class AppInstallProcessor @Inject constructor(
    ) {
        if (isItUpdateWork) {
            fusedDownload?.let {
                val packageStatus = fusedManagerRepository.getFusedDownloadPackageStatus(fusedDownload)
                val packageStatus =
                    fusedManagerRepository.getFusedDownloadPackageStatus(fusedDownload)

                if (packageStatus == Status.INSTALLED) {
                    UpdatesDao.addSuccessfullyUpdatedApp(it)
@@ -165,33 +154,44 @@ class AppInstallProcessor @Inject constructor(
        )
    }

    private suspend fun startAppInstallationProcess(
        fusedDownload: FusedDownload
    ): Boolean {
    private suspend fun startAppInstallationProcess(fusedDownload: FusedDownload) {
        if (fusedDownload.isAwaiting()) {
            fusedManagerRepository.downloadApp(fusedDownload)
            Timber.i("===> doWork: Download started ${fusedDownload.name} ${fusedDownload.status}")
        }

        isDownloading = true
        databaseRepository.getDownloadFlowById(fusedDownload.id)
            .transformWhile {
                emit(it)
                isInstallRunning(it)
            }.collect { latestFusedDownload ->
                handleFusedDownload(latestFusedDownload, fusedDownload)
            }
    }

    /**
         * observe app download/install process in a separate thread as DownloadManager download artifacts in a separate process
         * It checks install status every three seconds
     * Takes actions depending on the status of [FusedDownload]
     *
     * @param latestFusedDownload comes from Room database when [Status] is updated
     * @param fusedDownload is the original object when install process isn't started. It's used when [latestFusedDownload]
     * becomes null, After installation is completed.
     */
        tickerFlow(3.seconds)
            .onEach {
                val download = databaseRepository.getDownloadById(fusedDownload.id)
                if (download == null) {
    private suspend fun handleFusedDownload(
        latestFusedDownload: FusedDownload?,
        fusedDownload: FusedDownload
    ) {
        if (latestFusedDownload == null) {
            Timber.d("===> download null: finish installation")
            finishInstallation(fusedDownload)
                } else {
                    handleFusedDownloadStatusCheckingException(download)
            return
        }
            }.launchIn(CoroutineScope(Dispatchers.IO))
        Timber.d(">>> ===> doWork: Download started " + fusedDownload.name + " " + fusedDownload.status)
        return true

        handleFusedDownloadStatusCheckingException(latestFusedDownload)
    }

    private fun isInstallRunning(it: FusedDownload?) =
        it != null && it.status != Status.INSTALLATION_ISSUE

    private suspend fun handleFusedDownloadStatusCheckingException(
        download: FusedDownload
    ) {
@@ -204,19 +204,6 @@ class AppInstallProcessor @Inject constructor(
        }
    }

    /**
     * Triggers a repetitive event according to the delay passed in the parameter
     * @param period delay of each event
     * @param initialDelay initial delay to trigger the first event
     */
    private fun tickerFlow(period: Duration, initialDelay: Duration = Duration.ZERO) = flow {
        delay(initialDelay)
        while (isDownloading) {
            emit(Unit)
            delay(period)
        }
    }

    private suspend fun handleFusedDownloadStatus(fusedDownload: FusedDownload) {
        when (fusedDownload.status) {
            Status.AWAITING, Status.DOWNLOADING -> {
@@ -243,13 +230,5 @@ class AppInstallProcessor @Inject constructor(

    private suspend fun finishInstallation(fusedDownload: FusedDownload) {
        checkUpdateWork(fusedDownload)
        isDownloading = false
        unlockMutex()
    }

    private fun unlockMutex() {
        if (mutex.isLocked) {
            mutex.unlock()
        }
    }
}