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

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

add retry policy and show message for any error in update

parent 95c5ee72
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ dependencies {
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    implementation "android.arch.lifecycle:extensions:1.1.1"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

    // Coroutines
    def coroutines_version = "1.6.0"
+7 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ sealed class ResultSupreme<T> {
     * No valid data from processing.
     * Use [isUnknownError] to check.
     */
    class Error<T>() : ResultSupreme<T>() {
    open class Error<T>() : ResultSupreme<T>() {
        /**
         * @param message A String message to log or display to the user.
         * @param exception Optional exception from try-catch block.
@@ -85,6 +85,12 @@ sealed class ResultSupreme<T> {
        }
    }

    class WorkError<T> constructor(data: T, payload: Any? = null) : Error<T>(data) {
        init {
            this.otherPayload = payload
        }
    }

    /**
     * Data from processing. May be null.
     */
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import foundation.e.apps.api.gplay.utils.GPlayHttpClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject

class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpClient) {
+37 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
@@ -37,22 +38,30 @@ import foundation.e.apps.AppProgressViewModel
import foundation.e.apps.MainActivityViewModel
import foundation.e.apps.PrivacyInfoViewModel
import foundation.e.apps.R
import foundation.e.apps.api.ResultSupreme
import foundation.e.apps.api.fused.FusedAPIInterface
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.applicationlist.ApplicationListRVAdapter
import foundation.e.apps.databinding.FragmentUpdatesBinding
import foundation.e.apps.login.AuthObject
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.download.data.DownloadProgress
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.manager.workmanager.InstallWorkManager.INSTALL_WORK_NAME
import foundation.e.apps.updates.manager.UpdatesWorkManager
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.exceptions.GPlayException
import foundation.e.apps.utils.exceptions.GPlayLoginException
import foundation.e.apps.utils.modules.CommonUtilsModule.safeNavigate
import foundation.e.apps.utils.modules.PWAManagerModule
import foundation.e.apps.utils.parentFragment.TimeoutFragment
import foundation.e.apps.utils.toast
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import javax.inject.Inject

@@ -163,6 +172,28 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
                onTimeout()
            }*/
        }

        viewLifecycleOwner.lifecycleScope.launch {
            EventBus.events.flowWithLifecycle(viewLifecycleOwner.lifecycle)
                .filter { appEvent -> appEvent is AppEvent.UpdateEvent }.collectLatest {
                    val event = it.data as ResultSupreme.WorkError<*>
                    when (event.data) {
                        ResultStatus.USER_NOT_AVAILABLE -> {
                            requireContext().toast(getString(R.string.user_not_available))
                        }
                        ResultStatus.RETRY -> {
                            requireContext().toast(getString(R.string.message_retry))
                        }
                        else -> {
                            if (event.otherPayload is FusedDownload) {
                                requireContext().toast("${(event.otherPayload as FusedDownload).name} update is failed!")
                            } else {
                                requireContext().toast(getString(R.string.message_update_failed))
                            }
                        }
                    }
                }
        }
    }

    private fun showPurchasedAppMessage(fusedApp: FusedApp) {
@@ -272,7 +303,8 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
                    listOf(
                        WorkInfo.State.FAILED,
                        WorkInfo.State.BLOCKED,
                        WorkInfo.State.CANCELLED
                        WorkInfo.State.CANCELLED,
                        WorkInfo.State.SUCCEEDED
                    )
                if (!it.isNullOrEmpty() && errorStates.contains(it.last().state)) {
                    binding.button.isEnabled = true
+51 −14
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ import com.google.gson.Gson
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import foundation.e.apps.R
import foundation.e.apps.api.ResultSupreme
import foundation.e.apps.api.cleanapk.CleanAPKInterface
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
@@ -25,9 +26,13 @@ import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.manager.workmanager.InstallWorkManager
import foundation.e.apps.updates.UpdatesNotifier
import foundation.e.apps.utils.enums.Origin
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Type
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.modules.DataStoreModule
import kotlinx.coroutines.delay
import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.net.URL
@@ -52,6 +57,7 @@ class UpdatesWorker @AssistedInject constructor(
    private var automaticInstallEnabled = true
    private var onlyOnUnmeteredNetwork = false
    private var isAutoUpdate = true // indicates it is auto update or user initiated update
    private var retryCount = 0

    override suspend fun doWork(): Result {
        return try {
@@ -77,30 +83,39 @@ class UpdatesWorker @AssistedInject constructor(
        val appsNeededToUpdate = mutableListOf<FusedApp>()
        val user = getUser()
        val authData = getAuthData()

        var resultStatus = ResultStatus.OK
        if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) {
            /*
             * Signifies valid Google user and valid auth data to update
             * apps from Google Play store.
             * The user check will be more useful in No Google mode.
             */
            appsNeededToUpdate.addAll(updatesManagerRepository.getUpdates(authData).first)
            val updateData = updatesManagerRepository.getUpdates(authData)
            appsNeededToUpdate.addAll(updateData.first)
            resultStatus = updateData.second
        } else if (user != User.UNAVAILABLE) {
            /*
             * If authData is null, update apps from cleanapk only.
             */
            appsNeededToUpdate.addAll(updatesManagerRepository.getUpdatesOSS().first)
            val updateData = updatesManagerRepository.getUpdatesOSS()
            appsNeededToUpdate.addAll(updateData.first)
            resultStatus = updateData.second
        } else {
            /*
             * If user in UNAVAILABLE, don't do anything.
             */
            EventBus.invokeEvent(AppEvent.UpdateEvent(ResultSupreme.WorkError(ResultStatus.USER_NOT_AVAILABLE)))
            return
        }

        if (resultStatus != ResultStatus.OK) {
            manageRetry()
        } else {
            /*
         * Show notification only if enabled.
         * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5376
         */
            retryCount = 0
            if (isAutoUpdate && shouldShowNotification) {
                handleNotification(appsNeededToUpdate.size, isConnectedToUnmeteredNetwork)
            }
@@ -116,6 +131,20 @@ class UpdatesWorker @AssistedInject constructor(
                authData ?: AuthData("", ""),
            )
        }
    }

    private suspend fun manageRetry() {
        retryCount++
        if (retryCount == 1) {
            EventBus.invokeEvent(AppEvent.UpdateEvent(ResultSupreme.WorkError(ResultStatus.RETRY)))
        }
        if (retryCount <= 10) {
            delay(3000)
            checkForUpdates()
        } else {
            EventBus.invokeEvent(AppEvent.UpdateEvent(ResultSupreme.WorkError(ResultStatus.UNKNOWN)))
        }
    }

    private suspend fun triggerUpdateProcessOnSettings(
        isConnectedToUnmeteredNetwork: Boolean,
@@ -184,7 +213,15 @@ class UpdatesWorker @AssistedInject constructor(
            try {
                updateFusedDownloadWithAppDownloadLink(fusedApp, authData, fusedDownload)
            } catch (e: Exception) {
                e.printStackTrace()
                Timber.e(e)
                EventBus.invokeEvent(
                    AppEvent.UpdateEvent(
                        ResultSupreme.WorkError(
                            ResultStatus.UNKNOWN,
                            fusedDownload
                        )
                    )
                )
                return@forEach
            }

Loading