Show one single progress bar in the notification

Also don't show individual package results,
but a single dismissible status notification in the end.

Closes #59, #61
This commit is contained in:
Torsten Grote 2020-01-14 15:11:17 -03:00
parent 2bcf82d607
commit 6ed522bfb7
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
5 changed files with 67 additions and 18 deletions

View file

@ -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() {

View file

@ -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)

View file

@ -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? {

View file

@ -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) {

View file

@ -70,11 +70,14 @@
<!-- Notification -->
<string name="notification_channel_title">Backup Notification</string>
<string name="notification_title">Backup running</string>
<string name="notification_backup_starting">Starting Backup…</string>
<string name="notification_backup_result_complete">Backup complete</string>
<string name="notification_backup_result_rejected">Not backed up</string>
<string name="notification_backup_result_error">Backup failed</string>
<string name="notification_success_title">Backup finished</string>
<string name="notification_success_num_not_backed_up">%1$d apps could not get backed up</string>
<string name="notification_failed_title">Backup failed</string>
<string name="notification_error_channel_title">Error Notification</string>
<string name="notification_error_title">Backup Error</string>
<string name="notification_error_text">A device backup failed to run.</string>