Skip to content

Commit

Permalink
Update app install state after user comes back from manually installi…
Browse files Browse the repository at this point in the history
…ng 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.
  • Loading branch information
Torsten Grote authored and Chirayu Desai committed Oct 13, 2020
1 parent 1a81e2d commit a9402f4
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 11 deletions.
Expand Up @@ -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.
Expand Down
Expand Up @@ -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
Expand All @@ -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)
}
Expand All @@ -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
}
}
@@ -1,12 +1,15 @@
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
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
Expand Down Expand Up @@ -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<ApkInstallResult, CharSequence>() {
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
}
}

}
@@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit a9402f4

Please sign in to comment.