From a9402f46442055105457f5b7b1281dee6c4b2215 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 9 Oct 2020 14:00:52 -0300 Subject: [PATCH] Update app install state after user comes back from manually installing an app This way, the list of failed apps ideally keeps shrinking, allowing the user to see which apps are still left in a failed state. --- .../seedvault/restore/install/ApkInstaller.kt | 2 ++ .../restore/install/InstallIntentCreator.kt | 19 ++++++----- .../install/InstallProgressFragment.kt | 33 +++++++++++++++++-- .../restore/install/InstallResult.kt | 17 ++++++++++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkInstaller.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkInstaller.kt index c9ec4513..992c8245 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkInstaller.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkInstaller.kt @@ -59,6 +59,8 @@ internal class ApkInstaller(private val context: Context) { private fun install(cachedApk: File, installerPackageName: String?) { val sessionParams = SessionParams(MODE_FULL_INSTALL).apply { setInstallerPackageName(installerPackageName) + // Setting the INSTALL_ALLOW_TEST flag here does not allow us to install test apps, + // because the flag is filtered out by PackageInstallerService. } // Don't set more sessionParams intentionally here. // We saw strange permission issues when doing setInstallReason() or setting installFlags. diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallIntentCreator.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallIntentCreator.kt index 3bf3fcb2..0f10c746 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallIntentCreator.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallIntentCreator.kt @@ -3,7 +3,6 @@ package com.stevesoltys.seedvault.restore.install import android.content.Intent import android.content.Intent.ACTION_VIEW import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED import android.content.pm.PackageManager import android.net.Uri @@ -23,7 +22,7 @@ internal class InstallIntentCreator( fun getIntent(packageName: CharSequence, installerPackageName: CharSequence?): Intent { val i = Intent(ACTION_VIEW, Uri.parse("market://details?id=$packageName")).apply { - addFlags(FLAG_ACTIVITY_NEW_TASK) + // Not using FLAG_ACTIVITY_NEW_TASK, so startActivityForResult works addFlags(FLAG_ACTIVITY_CLEAR_TOP) addFlags(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) } @@ -39,14 +38,18 @@ internal class InstallIntentCreator( if (installerPackageName == null) return null val packageName = installerToPackage[installerPackageName] ?: return null val isInstalled = isPackageInstalled.getOrPut(packageName) { - try { - packageManager.getPackageInfo(packageName, 0) - true - } catch (e: PackageManager.NameNotFoundException) { - false - } + packageManager.isInstalled(packageName) } return if (isInstalled) packageName else null } } + +fun PackageManager.isInstalled(packageName: String): Boolean { + return try { + getPackageInfo(packageName, 0) + true + } catch (e: PackageManager.NameNotFoundException) { + false + } +} diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt index 4c8023c3..9388c7e2 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt @@ -1,5 +1,7 @@ package com.stevesoltys.seedvault.restore.install +import android.content.Context +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -7,6 +9,7 @@ import android.view.ViewGroup import android.widget.Button import android.widget.ProgressBar import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContract import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.Observer @@ -115,9 +118,33 @@ class InstallProgressFragment : Fragment(), InstallItemListener { } override fun onFailedItemClicked(item: ApkInstallResult) { - val i = - viewModel.installIntentCreator.getIntent(item.packageName, item.installerPackageName) - startActivity(i) + installAppLauncher.launch(item) + } + + private val installAppLauncher = registerForActivityResult(InstallApp()) { packageName -> + val result = viewModel.installResult.value ?: return@registerForActivityResult + if (result.isFinished) { + val changed = result.reCheckFailedPackage( + requireContext().packageManager, + packageName.toString() + ) + if (changed) adapter.update(result.getNotQueued()) + } + } + + private inner class InstallApp : ActivityResultContract() { + private lateinit var packageName: CharSequence + override fun createIntent(context: Context, input: ApkInstallResult): Intent { + packageName = input.packageName + return viewModel.installIntentCreator.getIntent( + input.packageName, + input.installerPackageName + ) + } + + override fun parseResult(resultCode: Int, intent: Intent?): CharSequence { + return packageName + } } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallResult.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallResult.kt index 51005651..b37cc851 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallResult.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallResult.kt @@ -1,9 +1,11 @@ package com.stevesoltys.seedvault.restore.install +import android.content.pm.PackageManager import android.graphics.drawable.Drawable import com.stevesoltys.seedvault.restore.install.ApkInstallState.FAILED import com.stevesoltys.seedvault.restore.install.ApkInstallState.IN_PROGRESS import com.stevesoltys.seedvault.restore.install.ApkInstallState.QUEUED +import com.stevesoltys.seedvault.restore.install.ApkInstallState.SUCCEEDED import java.util.concurrent.ConcurrentHashMap internal interface InstallResult { @@ -45,6 +47,12 @@ internal interface InstallResult { * and we need to treat all packages as failed that haven't been processed. */ fun queuedToFailed() + + /** + * Once [isFinished] is true, this can be called to re-check a package in state [FAILED]. + * If it is now installed, the state will be changed to [SUCCEEDED] and true returned. + */ + fun reCheckFailedPackage(pm: PackageManager, packageName: String): Boolean } internal class MutableInstallResult(override val total: Int) : InstallResult { @@ -89,6 +97,15 @@ internal class MutableInstallResult(override val total: Int) : InstallResult { return this } + override fun reCheckFailedPackage(pm: PackageManager, packageName: String): Boolean { + check(isFinished) { "re-checking failed packages only allowed when finished" } + if (pm.isInstalled(packageName)) { + update(packageName) { it.copy(state = SUCCEEDED) } + return true + } + return false + } + } data class ApkInstallResult(