When an app could not be restored, show the likely reason for it

This commit is contained in:
Torsten Grote 2020-01-13 18:17:08 -03:00
parent 74183d40d6
commit 22be36e2a7
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
7 changed files with 109 additions and 30 deletions

View file

@ -3,7 +3,6 @@ package com.stevesoltys.seedvault
import android.app.backup.BackupProgress
import android.app.backup.IBackupObserver
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
import android.util.Log
import android.util.Log.INFO
@ -13,9 +12,10 @@ import org.koin.core.inject
private val TAG = NotificationBackupObserver::class.java.simpleName
class NotificationBackupObserver(context: Context, private val userInitiated: Boolean) : IBackupObserver.Stub(), KoinComponent {
class NotificationBackupObserver(
private val context: Context,
private val userInitiated: Boolean) : IBackupObserver.Stub(), KoinComponent {
private val pm = context.packageManager
private val nm: BackupNotificationManager by inject()
/**
@ -63,15 +63,15 @@ class NotificationBackupObserver(context: Context, private val userInitiated: Bo
nm.onBackupFinished()
}
private fun getAppName(packageId: String): CharSequence = getAppName(pm, packageId)
private fun getAppName(packageId: String): CharSequence = getAppName(context, packageId)
}
fun getAppName(pm: PackageManager, packageId: String): CharSequence {
if (packageId == MAGIC_PACKAGE_MANAGER) return packageId
fun getAppName(context: Context, packageId: String): CharSequence {
if (packageId == MAGIC_PACKAGE_MANAGER) return context.getString(R.string.restore_magic_package)
return try {
val appInfo = pm.getApplicationInfo(packageId, 0)
pm.getApplicationLabel(appInfo) ?: packageId
val appInfo = context.packageManager.getApplicationInfo(packageId, 0)
context.packageManager.getApplicationLabel(appInfo) ?: packageId
} catch (e: NameNotFoundException) {
packageId
}

View file

@ -3,8 +3,7 @@ package com.stevesoltys.seedvault.restore
import android.content.pm.PackageManager.NameNotFoundException
import android.view.LayoutInflater
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.View.*
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
@ -14,9 +13,7 @@ import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.getAppName
import com.stevesoltys.seedvault.restore.AppRestoreStatus.IN_PROGRESS
import com.stevesoltys.seedvault.restore.AppRestoreStatus.SUCCEEDED
import com.stevesoltys.seedvault.restore.AppRestoreStatus.*
import com.stevesoltys.seedvault.restore.RestoreProgressAdapter.PackageViewHolder
import java.util.*
@ -64,38 +61,68 @@ internal class RestoreProgressAdapter : Adapter<PackageViewHolder>() {
private val pm = context.packageManager
private val appIcon: ImageView = v.findViewById(R.id.appIcon)
private val appName: TextView = v.findViewById(R.id.appName)
private val appInfo: TextView = v.findViewById(R.id.appInfo)
private val appStatus: ImageView = v.findViewById(R.id.appStatus)
private val progressBar: ProgressBar = v.findViewById(R.id.progressBar)
fun bind(item: AppRestoreResult) {
appName.text = item.name
if (item.packageName == MAGIC_PACKAGE_MANAGER) {
appIcon.setImageResource(R.drawable.ic_launcher_default)
appName.text = context.getString(R.string.restore_magic_package)
} else {
try {
appIcon.setImageDrawable(pm.getApplicationIcon(item.packageName))
} catch (e: NameNotFoundException) {
appIcon.setImageResource(R.drawable.ic_launcher_default)
}
appName.text = getAppName(pm, item.packageName)
}
if (item.status == IN_PROGRESS) {
appInfo.visibility = GONE
appStatus.visibility = INVISIBLE
progressBar.visibility = VISIBLE
} else {
appStatus.setImageResource(
if (item.status == SUCCEEDED) R.drawable.ic_check_green
else R.drawable.ic_cancel_red
)
appStatus.visibility = VISIBLE
progressBar.visibility = INVISIBLE
appInfo.visibility = GONE
when (item.status) {
SUCCEEDED -> {
appStatus.setImageResource(R.drawable.ic_check_green)
}
FAILED -> {
appStatus.setImageResource(R.drawable.ic_cancel_red)
}
else -> {
appStatus.setImageResource(R.drawable.ic_error_yellow)
appInfo.text = getInfo(item.status)
appInfo.visibility = VISIBLE
}
}
}
}
private fun getInfo(status: AppRestoreStatus): String = when(status) {
FAILED_NO_DATA -> context.getString(R.string.restore_app_no_data)
FAILED_NOT_ALLOWED -> context.getString(R.string.restore_app_not_allowed)
FAILED_NOT_INSTALLED -> context.getString(R.string.restore_app_not_installed)
FAILED_QUOTA_EXCEEDED -> context.getString(R.string.restore_app_quota_exceeded)
else -> "Please report a bug after you read this."
}
}
}
internal enum class AppRestoreStatus { IN_PROGRESS, SUCCEEDED, FAILED }
internal enum class AppRestoreStatus {
IN_PROGRESS,
SUCCEEDED,
FAILED,
FAILED_NO_DATA,
FAILED_NOT_ALLOWED,
FAILED_QUOTA_EXCEEDED,
FAILED_NOT_INSTALLED,
}
internal data class AppRestoreResult(val packageName: String, val status: AppRestoreStatus)
internal data class AppRestoreResult(
val packageName: String,
val name: CharSequence,
val status: AppRestoreStatus)

View file

@ -5,6 +5,7 @@ import android.app.backup.IBackupManager
import android.app.backup.IRestoreObserver
import android.app.backup.IRestoreSession
import android.app.backup.RestoreSet
import android.content.pm.PackageManager
import android.os.RemoteException
import android.os.UserHandle
import android.util.Log
@ -18,6 +19,8 @@ import com.stevesoltys.seedvault.BackupMonitor
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.crypto.KeyManager
import com.stevesoltys.seedvault.getAppName
import com.stevesoltys.seedvault.metadata.PackageState.*
import com.stevesoltys.seedvault.restore.AppRestoreStatus.*
import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_APPS
import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_BACKUP
@ -78,7 +81,7 @@ internal class RestoreViewModel(
private val mRestoreProgress = MutableLiveData<LinkedList<AppRestoreResult>>().apply {
value = LinkedList<AppRestoreResult>().apply {
add(AppRestoreResult(MAGIC_PACKAGE_MANAGER, IN_PROGRESS))
add(AppRestoreResult(MAGIC_PACKAGE_MANAGER, getAppName(app, MAGIC_PACKAGE_MANAGER), IN_PROGRESS))
}
}
internal val restoreProgress: LiveData<LinkedList<AppRestoreResult>> get() = mRestoreProgress
@ -180,19 +183,39 @@ internal class RestoreViewModel(
// check previous package first and change status
updateLatestPackage(list)
// add current package
list.addFirst(AppRestoreResult(packageName, IN_PROGRESS))
list.addFirst(AppRestoreResult(packageName, getAppName(app, packageName), IN_PROGRESS))
mRestoreProgress.postValue(list)
}
@WorkerThread
private fun updateLatestPackage(list: LinkedList<AppRestoreResult>) {
val latestResult = list[0]
if (restoreCoordinator.isFailedPackage(latestResult.packageName)) {
list[0] = latestResult.copy(status = FAILED)
list[0] = latestResult.copy(status = getFailedStatus(latestResult.packageName))
} else {
list[0] = latestResult.copy(status = SUCCEEDED)
}
}
@WorkerThread
private fun getFailedStatus(packageName: String, restorableBackup: RestorableBackup = chosenRestorableBackup.value!!): AppRestoreStatus {
val metadata = restorableBackup.packageMetadataMap[packageName] ?: return FAILED
return when (metadata.state) {
NO_DATA -> FAILED_NO_DATA
NOT_ALLOWED -> FAILED_NOT_ALLOWED
QUOTA_EXCEEDED -> FAILED_QUOTA_EXCEEDED
UNKNOWN_ERROR -> FAILED
APK_AND_DATA -> {
try {
app.packageManager.getPackageInfo(packageName, 0)
FAILED
} catch (e: PackageManager.NameNotFoundException) {
FAILED_NOT_INSTALLED
}
}
}
}
@WorkerThread
private fun onRestoreComplete(result: RestoreBackupResult) {
// update status of latest package
@ -205,7 +228,9 @@ internal class RestoreViewModel(
val expectedPackages = restorableBackup.packageMetadataMap.keys
expectedPackages.removeAll(seenPackages)
for (packageName: String in expectedPackages) {
list.addFirst(AppRestoreResult(packageName, FAILED))
// TODO don't add if it was a NO_DATA system app
val failedStatus = getFailedStatus(packageName, restorableBackup)
list.addFirst(AppRestoreResult(packageName, getAppName(app, packageName), failedStatus))
}
mRestoreProgress.postValue(list)

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/yellow"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z" />
</vector>

View file

@ -25,16 +25,29 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@+id/appInfo"
app:layout_constraintEnd_toStartOf="@+id/appStatus"
app:layout_constraintStart_toEndOf="@+id/appIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Seedvault Backup" />
<TextView
android:id="@+id/appInfo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/appName"
app:layout_constraintStart_toStartOf="@+id/appName"
app:layout_constraintTop_toBottomOf="@+id/appName"
tools:text="Some additional information about why the app could not be installed or its data not restored."
tools:visibility="visible" />
<ImageView
android:id="@+id/appStatus"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="32dp"
android:layout_height="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -44,8 +57,8 @@
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="32dp"
android:layout_height="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View file

@ -4,4 +4,5 @@
<color name="divider">#8A000000</color>
<color name="green">#558B2F</color>
<color name="red">#D32F2F</color>
<color name="yellow">#F9A825</color>
</resources>

View file

@ -92,6 +92,10 @@
<string name="restore_next">Next</string>
<string name="restore_restoring">Restoring Backup</string>
<string name="restore_magic_package">System Package Manager</string>
<string name="restore_app_no_data">App reported no data for backup</string>
<string name="restore_app_not_allowed">Data backup was not allowed</string>
<string name="restore_app_not_installed">App not installed</string>
<string name="restore_app_quota_exceeded">Backup quota exceeded</string>
<string name="restore_finished_success">Restore complete</string>
<string name="restore_finished_error">An error occurred while restoring the backup.</string>
<string name="restore_finished_button">Finish</string>