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

Commit 8ea14cbc authored by Hasib Prince's avatar Hasib Prince
Browse files

Merge branch '1836-improve_download_event' into 'release-1.21-rc'

improved handling event from downloadManager

See merge request !430
parents f352a435 65748ae1
Loading
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -83,6 +83,15 @@
            </intent-filter>
        </receiver>

        <receiver android:name=".receivers.DumpAppInstallStatusReceiver"
            android:enabled="true"
            android:exported="true"
            tools:ignore="ExportedReceiver">
            <intent-filter>
                <action android:name="foundation.e.apps.action.APP_INSTALL_STATE"/>
            </intent-filter>
        </receiver>

        <!-- TODO: ExportedReceiver, suppressing because changes are needed in other apps -->
        <receiver android:name=".install.receiver.PWAPlayerStatusReceiver"
            tools:ignore="ExportedReceiver"
+6 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import androidx.work.ExistingPeriodicWorkPolicy
import dagger.hilt.android.HiltAndroidApp
import foundation.e.apps.data.Constants.TAG_APP_INSTALL_STATE
import foundation.e.apps.data.Constants.TAG_AUTHDATA_DUMP
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.data.preference.AppLoungePreference
@@ -90,7 +91,11 @@ class AppLoungeApplication : Application(), Configuration.Provider {
            Telemetry.init(BuildConfig.SENTRY_DSN, this)
            plant(object : Timber.Tree() {
                override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
                    if (priority <= Log.WARN && tag != TAG_AUTHDATA_DUMP) {
                    if (priority <= Log.WARN && !listOf(
                            TAG_AUTHDATA_DUMP,
                            TAG_APP_INSTALL_STATE
                        ).contains(tag)
                    ) {
                        return
                    }
                    Log.println(priority, tag, message)
+3 −0
Original line number Diff line number Diff line
@@ -9,4 +9,7 @@ object Constants {

    const val ACTION_AUTHDATA_DUMP = "foundation.e.apps.action.DUMP_GACCOUNT_INFO"
    const val TAG_AUTHDATA_DUMP = "AUTHDATA_DUMP"

    const val ACTION_DUMP_APP_INSTALL_STATE = "foundation.e.apps.action.APP_INSTALL_STATE"
    const val TAG_APP_INSTALL_STATE = "APP_INSTALL_STATE"
}
+50 −20
Original line number Diff line number Diff line
@@ -29,14 +29,16 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload
import foundation.e.apps.install.notification.StorageNotificationManager
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import android.app.DownloadManager as PlatformDownloadManager
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
@@ -45,12 +47,13 @@ class DownloadManagerUtils @Inject constructor(
    private val fusedManagerRepository: FusedManagerRepository,
    private val downloadManager: DownloadManager,
    private val storageNotificationManager: StorageNotificationManager,
    @Named("ioCoroutineScope") private val coroutineScope: CoroutineScope
) {
    private val mutex = Mutex()

    @DelicateCoroutinesApi
    fun cancelDownload(downloadId: Long) {
        GlobalScope.launch {
        coroutineScope.launch {
            val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId)
            fusedManagerRepository.cancelDownload(fusedDownload)
        }
@@ -58,27 +61,25 @@ class DownloadManagerUtils @Inject constructor(

    @DelicateCoroutinesApi
    fun updateDownloadStatus(downloadId: Long) {
        GlobalScope.launch {
        coroutineScope.launch {
            mutex.withLock {
                delay(1500) // Waiting for downloadmanager to publish the progress of last bytes
                val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId)
                if (fusedDownload.id.isNotEmpty()) {
                    updateDownloadIdMap(fusedDownload, downloadId)
                    val numberOfDownloadedItems =
                        fusedDownload.downloadIdMap.values.filter { it }.size
                    Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}")

                    if (downloadManager.hasDownloadFailed(downloadId)) {
                        handleDownloadFailed(fusedDownload, downloadId)
                        Timber.e(
                            "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${downloadManager.getDownloadFailureReason(downloadId)}"
                            "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${
                                downloadManager.getDownloadFailureReason(
                                    downloadId
                                )
                            }"
                        )
                        return@launch
                    }

                    if (validateDownload(numberOfDownloadedItems, fusedDownload, downloadId)) {
                        handleDownloadSuccess(fusedDownload)
                    }
                    validateDownload(fusedDownload, downloadId)
                }
            }
        }
@@ -87,9 +88,11 @@ class DownloadManagerUtils @Inject constructor(
    private suspend fun handleDownloadSuccess(fusedDownload: FusedDownload) {
        Timber.i("===> Download is completed for: ${fusedDownload.name}")
        fusedManagerRepository.moveOBBFileToOBBDirectory(fusedDownload)
        if (fusedDownload.status == Status.DOWNLOADING) {
            fusedDownload.status = Status.DOWNLOADED
            fusedManagerRepository.updateFusedDownload(fusedDownload)
        }
    }

    private suspend fun handleDownloadFailed(fusedDownload: FusedDownload, downloadId: Long) {
        fusedManagerRepository.installationIssue(fusedDownload)
@@ -103,20 +106,47 @@ class DownloadManagerUtils @Inject constructor(
    }

    private suspend fun validateDownload(
        numberOfDownloadedItems: Int,
        fusedDownload: FusedDownload,
        downloadId: Long
    ): Boolean {
    ) {
        val incompleteDownloadState = listOf(
            PlatformDownloadManager.STATUS_PENDING,
            PlatformDownloadManager.STATUS_RUNNING,
            PlatformDownloadManager.STATUS_PAUSED,
        )

        val isDownloadSuccessful = downloadManager.isDownloadSuccessful(downloadId)

        if (isDownloadSuccessful.first) {
            updateDownloadIdMap(fusedDownload, downloadId)
        }

        val numberOfDownloadedItems =
            fusedDownload.downloadIdMap.values.filter { it }.size
        Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}")

        // if download status code is unknown (-1), consider installation is failed.
        if (isDownloadSuccessful.second == -1) {
            handleDownloadFailed(fusedDownload, downloadId)
        val areAllFilesDownloaded = areAllFilesDownloaded(
            numberOfDownloadedItems,
            fusedDownload
        )

        if (isDownloadSuccessful.first && areAllFilesDownloaded && checkCleanApkSignatureOK(fusedDownload)) {
            handleDownloadSuccess(fusedDownload)
            return
        }

        return isDownloadSuccessful.first && areAllFilesDownloaded(
            numberOfDownloadedItems, fusedDownload
        ) && checkCleanApkSignatureOK(fusedDownload)
        if (incompleteDownloadState.contains(isDownloadSuccessful.second)
            || (isDownloadSuccessful.first && !areAllFilesDownloaded)
        ) {
            return
        }

        handleDownloadFailed(fusedDownload, downloadId)
        Timber.e(
            "Download failed for ${fusedDownload.packageName}: " +
                    "Download Status: ${isDownloadSuccessful.second}"
        )
    }

    private fun areAllFilesDownloaded(
+19 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context
import com.aurora.gplayapi.exceptions.ApiException
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.R
import foundation.e.apps.data.DownloadManager
import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.enums.Status
@@ -34,6 +35,7 @@ import foundation.e.apps.data.fusedDownload.FusedManagerRepository
import foundation.e.apps.data.fusedDownload.models.FusedDownload
import foundation.e.apps.data.playstore.utils.GplayHttpRequestException
import foundation.e.apps.data.preference.DataStoreManager
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.StorageComputer
@@ -41,6 +43,7 @@ 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.DelicateCoroutinesApi
import kotlinx.coroutines.flow.transformWhile
import timber.log.Timber
import java.text.NumberFormat
@@ -53,9 +56,12 @@ class AppInstallProcessor @Inject constructor(
    private val fusedManagerRepository: FusedManagerRepository,
    private val applicationRepository: ApplicationRepository,
    private val dataStoreManager: DataStoreManager,
    private val storageNotificationManager: StorageNotificationManager
    private val storageNotificationManager: StorageNotificationManager,
) {

    @Inject
    lateinit var downloadManager: DownloadManagerUtils

    private var isItUpdateWork = false

    companion object {
@@ -197,6 +203,7 @@ class AppInstallProcessor @Inject constructor(
        )
    }

    @OptIn(DelicateCoroutinesApi::class)
    suspend fun processInstall(
        fusedDownloadId: String,
        isItUpdateWork: Boolean,
@@ -211,6 +218,8 @@ class AppInstallProcessor @Inject constructor(

            fusedDownload?.let {

                checkDownloadingState(fusedDownload)

                this.isItUpdateWork =
                    isItUpdateWork && fusedManagerRepository.isFusedDownloadInstalled(fusedDownload)

@@ -248,6 +257,15 @@ class AppInstallProcessor @Inject constructor(
        return Result.success(ResultStatus.OK)
    }

    @OptIn(DelicateCoroutinesApi::class)
    private fun checkDownloadingState(fusedDownload: FusedDownload) {
        if (fusedDownload.status == Status.DOWNLOADING) {
            fusedDownload.downloadIdMap.keys.forEach { downloadId ->
                downloadManager.updateDownloadStatus(downloadId)
            }
        }
    }

    private fun areFilesDownloadedButNotInstalled(fusedDownload: FusedDownload) =
        fusedDownload.areFilesDownloaded() && (!fusedManagerRepository.isFusedDownloadInstalled(
            fusedDownload
Loading