diff --git a/app/src/main/java/com/stevesoltys/seedvault/BackupNotificationManager.kt b/app/src/main/java/com/stevesoltys/seedvault/BackupNotificationManager.kt
index d48b196e..561f44cb 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/BackupNotificationManager.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/BackupNotificationManager.kt
@@ -63,6 +63,8 @@ class BackupNotificationManager(private val context: Context) {
val notification = observerBuilder.apply {
setContentTitle(context.getString(R.string.notification_title))
setContentText(app)
+ setOngoing(true)
+ setShowWhen(false)
setWhen(System.currentTimeMillis())
setProgress(expected, transferred, false)
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
@@ -79,14 +81,33 @@ class BackupNotificationManager(private val context: Context) {
val notification = observerBuilder.apply {
setContentTitle(title)
setContentText(app)
+ setOngoing(true)
+ setShowWhen(false)
setWhen(System.currentTimeMillis())
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
}.build()
nm.notify(NOTIFICATION_ID_OBSERVER, notification)
}
- fun onBackupFinished() {
- nm.cancel(NOTIFICATION_ID_OBSERVER)
+ fun onBackupFinished(success: Boolean, notBackedUp: Int?, userInitiated: Boolean) {
+ if (!userInitiated) {
+ nm.cancel(NOTIFICATION_ID_OBSERVER)
+ return
+ }
+ val titleRes = if (success) R.string.notification_success_title else R.string.notification_failed_title
+ val contentText = if (notBackedUp == null) null else {
+ context.getString(R.string.notification_success_num_not_backed_up, notBackedUp)
+ }
+ val notification = observerBuilder.apply {
+ setContentTitle(context.getString(titleRes))
+ setContentText(contentText)
+ setOngoing(false)
+ setShowWhen(true)
+ setWhen(System.currentTimeMillis())
+ setProgress(0, 0, false)
+ priority = PRIORITY_LOW
+ }.build()
+ nm.notify(NOTIFICATION_ID_OBSERVER, notification)
}
fun onBackupError() {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/NotificationBackupObserver.kt b/app/src/main/java/com/stevesoltys/seedvault/NotificationBackupObserver.kt
index c3394065..b579d4f7 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/NotificationBackupObserver.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/NotificationBackupObserver.kt
@@ -7,6 +7,7 @@ import android.content.pm.PackageManager.NameNotFoundException
import android.util.Log
import android.util.Log.INFO
import android.util.Log.isLoggable
+import com.stevesoltys.seedvault.metadata.MetadataManager
import org.koin.core.KoinComponent
import org.koin.core.inject
@@ -14,9 +15,18 @@ private val TAG = NotificationBackupObserver::class.java.simpleName
class NotificationBackupObserver(
private val context: Context,
+ private val expectedPackages: Int,
private val userInitiated: Boolean) : IBackupObserver.Stub(), KoinComponent {
private val nm: BackupNotificationManager by inject()
+ private val metadataManager: MetadataManager by inject()
+ private var currentPackage: String? = null
+ private var numPackages: Int = 0
+
+ init {
+ // we need to show this manually as [onUpdate] isn't called for first @pm@ package
+ nm.onBackupUpdate(getAppName(MAGIC_PACKAGE_MANAGER), 0, expectedPackages, userInitiated)
+ }
/**
* This method could be called several times for packages with full data backup.
@@ -26,10 +36,7 @@ class NotificationBackupObserver(
* @param backupProgress Current progress of backup for the package.
*/
override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
- val transferred = backupProgress.bytesTransferred.toInt()
- val expected = backupProgress.bytesExpected.toInt()
- val app = getAppName(currentBackupPackage)
- nm.onBackupUpdate(app, transferred, expected, userInitiated)
+ showProgressNotification(currentBackupPackage)
}
/**
@@ -46,7 +53,8 @@ class NotificationBackupObserver(
if (isLoggable(TAG, INFO)) {
Log.i(TAG, "Completed. Target: $target, status: $status")
}
- nm.onBackupResult(getAppName(target), status, userInitiated)
+ // often [onResult] gets called right away without any [onUpdate] call
+ showProgressNotification(target)
}
/**
@@ -58,9 +66,23 @@ class NotificationBackupObserver(
*/
override fun backupFinished(status: Int) {
if (isLoggable(TAG, INFO)) {
- Log.i(TAG, "Backup finished. Status: $status")
+ Log.i(TAG, "Backup finished $numPackages/$expectedPackages. Status: $status")
}
- nm.onBackupFinished()
+ val success = status == 0
+ val notBackedUp = if (success) metadataManager.getPackagesNumNotBackedUp() else null
+ nm.onBackupFinished(success, notBackedUp, userInitiated)
+ }
+
+ private fun showProgressNotification(packageName: String) {
+ if (currentPackage == packageName) return
+
+ if (isLoggable(TAG, INFO)) {
+ Log.i(TAG, "Showing progress notification for $currentPackage $numPackages/$expectedPackages")
+ }
+ currentPackage = packageName
+ val app = getAppName(packageName)
+ numPackages += 1
+ nm.onBackupUpdate(app, numPackages, expectedPackages, userInitiated)
}
private fun getAppName(packageId: String): CharSequence = getAppName(context, packageId)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
index 799b13cd..2ef14ef5 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
@@ -178,6 +178,13 @@ class MetadataManager(
return metadata.packageMetadataMap[packageName]?.copy()
}
+ @Synchronized
+ fun getPackagesNumNotBackedUp(): Int {
+ return metadata.packageMetadataMap.filter { (_, packageMetadata) ->
+ !packageMetadata.system && packageMetadata.state != APK_AND_DATA
+ }.count()
+ }
+
@Synchronized
@VisibleForTesting
private fun getMetadataFromCache(): BackupMetadata? {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt
index f4392134..688f1026 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/ConfigurableBackupTransportService.kt
@@ -15,7 +15,6 @@ import androidx.annotation.WorkerThread
import com.stevesoltys.seedvault.BackupMonitor
import com.stevesoltys.seedvault.BackupNotificationManager
import com.stevesoltys.seedvault.NotificationBackupObserver
-import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.transport.backup.PackageService
import org.koin.core.context.GlobalContext.get
@@ -52,19 +51,16 @@ class ConfigurableBackupTransportService : Service() {
@WorkerThread
fun requestBackup(context: Context) {
- // show notification
- val nm: BackupNotificationManager = get().koin.get()
- nm.onBackupUpdate(context.getString(R.string.notification_backup_starting), 0, 1, true)
-
val packageService: PackageService = get().koin.get()
- val observer = NotificationBackupObserver(context, true)
- val flags = FLAG_NON_INCREMENTAL_BACKUP or FLAG_USER_INITIATED
val packages = packageService.eligiblePackages
+
+ val observer = NotificationBackupObserver(context, packages.size, true)
val result = try {
val backupManager: IBackupManager = get().koin.get()
- backupManager.requestBackup(packages, observer, BackupMonitor(), flags)
+ backupManager.requestBackup(packages, observer, BackupMonitor(), FLAG_USER_INITIATED)
} catch (e: RemoteException) {
Log.e(TAG, "Error during backup: ", e)
+ val nm: BackupNotificationManager = get().koin.get()
nm.onBackupError()
}
if (result == BackupManager.SUCCESS) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cde0d8c1..bc3132ca 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -70,11 +70,14 @@
Backup Notification
Backup running
- Starting Backup…
Backup complete
Not backed up
Backup failed
+ Backup finished
+ %1$d apps could not get backed up
+ Backup failed
+
Error Notification
Backup Error
A device backup failed to run.