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

Commit ecd244d3 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

MR 10 - Adapt Updates fragment

parent 36864b3f
Loading
Loading
Loading
Loading
+73 −14
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ package foundation.e.apps.updates
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
@@ -29,34 +30,35 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkInfo
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.aurora.gplayapi.data.models.AuthData
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.AppInfoFetchViewModel
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.fused.FusedAPIImpl
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.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.exceptions.GPlayException
import foundation.e.apps.utils.exceptions.GPlayValidationException
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.parentFragment.TimeoutFragment2
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInterface {
class UpdatesFragment : TimeoutFragment2(R.layout.fragment_updates), FusedAPIInterface {

    private var _binding: FragmentUpdatesBinding? = null
    private val binding get() = _binding!!
@@ -85,7 +87,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
         * Explanation of double observers in HomeFragment.kt
         */

        mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) {
        /*mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) {
            if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) {
                return@observe
            }
@@ -93,6 +95,20 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
        }
        mainActivityViewModel.authData.observe(viewLifecycleOwner) {
            refreshDataOrRefreshToken(mainActivityViewModel)
        }*/

        setupListening()

        authObjects.observe(viewLifecycleOwner) {
            if (it == null) return@observe
            if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) {
                return@observe
            }
            loadData(it)
        }

        updatesViewModel.exceptionsLiveData.observe(viewLifecycleOwner) {
            handleExceptionsCommon(it)
        }

        val recyclerView = binding.recyclerView
@@ -144,9 +160,9 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
                    }
                }

            if (it.second != ResultStatus.OK) {
            /*if (it.second != ResultStatus.OK) {
                onTimeout()
            }
            }*/
        }
    }

@@ -166,7 +182,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
        ).show(childFragmentManager, "UpdatesFragment")
    }

    override fun onTimeout() {
    /*override fun onTimeout() {
        if (!isTimeoutDialogDisplayed()) {
            stopLoadingUI()
            displayTimeoutAlertDialog(
@@ -194,11 +210,54 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
                allowCancel = true,
            )
        }
    }*/

    override fun onTimeout(
        exception: Exception,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog.apply {
            if (exception is GPlayException) {
                setMessage(R.string.timeout_desc_gplay)
                setNegativeButton(R.string.open_settings) { _, _ ->
                    openSettings()
                }
            } else {
                setMessage(R.string.timeout_desc_cleanapk)
            }
        }
    }

    override fun onSignInError(
        exception: GPlayValidationException,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog.apply {
            setNegativeButton(R.string.open_settings) { _, _ ->
                openSettings()
            }
        }
    }

    override fun onDataLoadError(
        exception: Exception,
        predefinedDialog: AlertDialog.Builder
    ): AlertDialog.Builder? {
        return predefinedDialog.apply {
            if (exception is GPlayException) {
                setNegativeButton(R.string.open_settings) { _, _ ->
                    openSettings()
                }
            }
        }
    }

    override fun refreshData(authData: AuthData) {
    override fun loadData(authObjectList: List<AuthObject>) {
        showLoadingUI()
        updatesViewModel.getUpdates(authData)
        updatesViewModel.loadData(authObjectList) {
            clearAndRestartGPlayLogin()
            true
        }
        binding.button.setOnClickListener {
            UpdatesWorkManager.startUpdateAllWork(requireContext().applicationContext)
            observeUpdateWork()
@@ -222,14 +281,14 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
            }
    }

    private fun showLoadingUI() {
    override fun showLoadingUI() {
        binding.button.isEnabled = false
        binding.noUpdates.visibility = View.GONE
        binding.progressBar.visibility = View.VISIBLE
        binding.recyclerView.visibility = View.INVISIBLE
    }

    private fun stopLoadingUI() {
    override fun stopLoadingUI() {
        binding.progressBar.visibility = View.GONE
        binding.recyclerView.visibility = View.VISIBLE
    }
@@ -239,7 +298,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte
        appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) {
            updateProgressOfDownloadingItems(binding.recyclerView, it)
        }
        resetTimeoutDialogLock()
//        resetTimeoutDialogLock()
    }

    private fun observeDownloadList() {
+45 −4
Original line number Diff line number Diff line
@@ -19,16 +19,19 @@
package foundation.e.apps.updates

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.WorkInfo
import com.aurora.gplayapi.data.models.AuthData
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.login.AuthObject
import foundation.e.apps.updates.manager.UpdatesManagerRepository
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.exceptions.CleanApkException
import foundation.e.apps.utils.exceptions.GPlayException
import foundation.e.apps.utils.parentFragment.LoadingViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@@ -36,14 +39,52 @@ import javax.inject.Inject
class UpdatesViewModel @Inject constructor(
    private val updatesManagerRepository: UpdatesManagerRepository,
    private val fusedAPIRepository: FusedAPIRepository
) : ViewModel() {
) : LoadingViewModel() {

    val updatesList: MutableLiveData<Pair<List<FusedApp>, ResultStatus?>> = MutableLiveData()

    fun getUpdates(authData: AuthData) {
    fun loadData(
        authObjectList: List<AuthObject>,
        retryBlock: (failedObjects: List<AuthObject>) -> Boolean,
    ) {
        super.onLoadData(authObjectList, { successAuthList, _ ->

            successAuthList.find { it is AuthObject.GPlayAuth }?.run {
                getUpdates(result.data!! as AuthData)
                return@onLoadData
            }

            successAuthList.find { it is AuthObject.CleanApk }?.run {
                getUpdates(AuthData("", ""))
                return@onLoadData
            }
        }, retryBlock)
    }

    fun getUpdates(authData: AuthData?) {
        viewModelScope.launch {
            val updatesResult = updatesManagerRepository.getUpdates(authData)
            val updatesResult = if (authData != null)
                updatesManagerRepository.getUpdates(authData)
            else updatesManagerRepository.getUpdatesOSS()
            updatesList.postValue(updatesResult)

            if (updatesResult.second != ResultStatus.OK) {
                val exception =
                    if (authData != null &&
                        (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank())
                    ) {
                        GPlayException(
                            updatesResult.second == ResultStatus.TIMEOUT,
                            "Data load error"
                        )
                    } else CleanApkException(
                        updatesResult.second == ResultStatus.TIMEOUT,
                        "Data load error"
                    )

                exceptionsList.add(exception)
                exceptionsLiveData.postValue(exceptionsList)
            }
        }
    }

+31 −0
Original line number Diff line number Diff line
@@ -83,6 +83,37 @@ class UpdatesManagerImpl @Inject constructor(
        return Pair(nonFaultyUpdateList, status)
    }

    suspend fun getUpdatesOSS(): Pair<List<FusedApp>, ResultStatus> {
        val pkgList = mutableListOf<String>()
        val updateList = mutableListOf<FusedApp>()
        var status = ResultStatus.OK

        val userApplications = pkgManagerModule.getAllUserApps()
        userApplications.forEach { pkgList.add(it.packageName) }

        if (pkgList.isNotEmpty()) {
            // Get updates from CleanAPK
            val cleanAPKResult = fusedAPIRepository.getApplicationDetails(
                pkgList,
                AuthData("", ""),
                Origin.CLEANAPK
            )
            cleanAPKResult.first.forEach {
                if (it.status == Status.UPDATABLE && it.filterLevel.isUnFiltered()) updateList.add(
                    it
                )
            }
            cleanAPKResult.second.let {
                if (it != ResultStatus.OK) {
                    status = it
                }
            }
        }

        val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList)
        return Pair(nonFaultyUpdateList, status)
    }

    fun getApplicationCategoryPreference(): String {
        return fusedAPIRepository.getApplicationCategoryPreference()
    }
+4 −0
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@ class UpdatesManagerRepository @Inject constructor(
        }
    }

    suspend fun getUpdatesOSS(): Pair<List<FusedApp>, ResultStatus> {
        return updatesManagerImpl.getUpdatesOSS()
    }

    fun getApplicationCategoryPreference(): String {
        return updatesManagerImpl.getApplicationCategoryPreference()
    }
+40 −4
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ 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.Type
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.modules.DataStoreModule
import timber.log.Timber
import java.io.ByteArrayOutputStream
@@ -66,11 +67,40 @@ class UpdatesWorker @AssistedInject constructor(
        }
    }

    private fun getUser(): User {
        return dataStoreModule.getUserType()
    }

    private suspend fun checkForUpdates() {
        loadSettings()
        val isConnectedToUnmeteredNetwork = isConnectedToUnmeteredNetwork(applicationContext)
        val appsNeededToUpdate = mutableListOf<FusedApp>()
        val user = getUser()
        val authData = getAuthData()
        val appsNeededToUpdate = updatesManagerRepository.getUpdates(authData).first

        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)
        } else if (user != User.UNAVAILABLE) {
            /*
             * If authData is null, update apps from cleanapk only.
             */
            appsNeededToUpdate.addAll(updatesManagerRepository.getUpdatesOSS().first)
        } else {
            /*
             * If user in UNAVAILABLE, don't do anything.
             */
            return
        }

        /*
         * Show notification only if enabled.
         * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5376
         */
        if (isAutoUpdate && shouldShowNotification) {
            handleNotification(appsNeededToUpdate.size, isConnectedToUnmeteredNetwork)
        }
@@ -78,7 +108,12 @@ class UpdatesWorker @AssistedInject constructor(
        triggerUpdateProcessOnSettings(
            isConnectedToUnmeteredNetwork,
            appsNeededToUpdate,
            authData
            /*
             * If authData is null, only cleanApk data will be present
             * in appsNeededToUpdate list. Hence it is safe to proceed with
             * blank AuthData.
             */
            authData ?: AuthData("", ""),
        )
    }

@@ -113,9 +148,10 @@ class UpdatesWorker @AssistedInject constructor(
        }
    }

    private fun getAuthData(): AuthData {
    private fun getAuthData(): AuthData? {
        val authDataJson = dataStoreModule.getAuthDataSync()
        return gson.fromJson(authDataJson, AuthData::class.java)
        return if (authDataJson.isBlank()) return null
        else gson.fromJson(authDataJson, AuthData::class.java)
    }

    private suspend fun startUpdateProcess(