When an app could not be restored, show the likely reason for it
This commit is contained in:
parent
74183d40d6
commit
22be36e2a7
7 changed files with 109 additions and 30 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
9
app/src/main/res/drawable/ic_error_yellow.xml
Normal file
9
app/src/main/res/drawable/ic_error_yellow.xml
Normal 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>
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue