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:
parent
2bcf82d607
commit
6ed522bfb7
5 changed files with 67 additions and 18 deletions
|
@ -63,6 +63,8 @@ class BackupNotificationManager(private val context: Context) {
|
||||||
val notification = observerBuilder.apply {
|
val notification = observerBuilder.apply {
|
||||||
setContentTitle(context.getString(R.string.notification_title))
|
setContentTitle(context.getString(R.string.notification_title))
|
||||||
setContentText(app)
|
setContentText(app)
|
||||||
|
setOngoing(true)
|
||||||
|
setShowWhen(false)
|
||||||
setWhen(System.currentTimeMillis())
|
setWhen(System.currentTimeMillis())
|
||||||
setProgress(expected, transferred, false)
|
setProgress(expected, transferred, false)
|
||||||
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
|
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
|
||||||
|
@ -79,14 +81,33 @@ class BackupNotificationManager(private val context: Context) {
|
||||||
val notification = observerBuilder.apply {
|
val notification = observerBuilder.apply {
|
||||||
setContentTitle(title)
|
setContentTitle(title)
|
||||||
setContentText(app)
|
setContentText(app)
|
||||||
|
setOngoing(true)
|
||||||
|
setShowWhen(false)
|
||||||
setWhen(System.currentTimeMillis())
|
setWhen(System.currentTimeMillis())
|
||||||
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
|
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
|
||||||
}.build()
|
}.build()
|
||||||
nm.notify(NOTIFICATION_ID_OBSERVER, notification)
|
nm.notify(NOTIFICATION_ID_OBSERVER, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBackupFinished() {
|
fun onBackupFinished(success: Boolean, notBackedUp: Int?, userInitiated: Boolean) {
|
||||||
nm.cancel(NOTIFICATION_ID_OBSERVER)
|
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() {
|
fun onBackupError() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.content.pm.PackageManager.NameNotFoundException
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import android.util.Log.isLoggable
|
import android.util.Log.isLoggable
|
||||||
|
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
import org.koin.core.inject
|
import org.koin.core.inject
|
||||||
|
|
||||||
|
@ -14,9 +15,18 @@ private val TAG = NotificationBackupObserver::class.java.simpleName
|
||||||
|
|
||||||
class NotificationBackupObserver(
|
class NotificationBackupObserver(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val expectedPackages: Int,
|
||||||
private val userInitiated: Boolean) : IBackupObserver.Stub(), KoinComponent {
|
private val userInitiated: Boolean) : IBackupObserver.Stub(), KoinComponent {
|
||||||
|
|
||||||
private val nm: BackupNotificationManager by inject()
|
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.
|
* 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.
|
* @param backupProgress Current progress of backup for the package.
|
||||||
*/
|
*/
|
||||||
override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
|
override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
|
||||||
val transferred = backupProgress.bytesTransferred.toInt()
|
showProgressNotification(currentBackupPackage)
|
||||||
val expected = backupProgress.bytesExpected.toInt()
|
|
||||||
val app = getAppName(currentBackupPackage)
|
|
||||||
nm.onBackupUpdate(app, transferred, expected, userInitiated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +53,8 @@ class NotificationBackupObserver(
|
||||||
if (isLoggable(TAG, INFO)) {
|
if (isLoggable(TAG, INFO)) {
|
||||||
Log.i(TAG, "Completed. Target: $target, status: $status")
|
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) {
|
override fun backupFinished(status: Int) {
|
||||||
if (isLoggable(TAG, INFO)) {
|
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)
|
private fun getAppName(packageId: String): CharSequence = getAppName(context, packageId)
|
||||||
|
|
|
@ -178,6 +178,13 @@ class MetadataManager(
|
||||||
return metadata.packageMetadataMap[packageName]?.copy()
|
return metadata.packageMetadataMap[packageName]?.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun getPackagesNumNotBackedUp(): Int {
|
||||||
|
return metadata.packageMetadataMap.filter { (_, packageMetadata) ->
|
||||||
|
!packageMetadata.system && packageMetadata.state != APK_AND_DATA
|
||||||
|
}.count()
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
private fun getMetadataFromCache(): BackupMetadata? {
|
private fun getMetadataFromCache(): BackupMetadata? {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.annotation.WorkerThread
|
||||||
import com.stevesoltys.seedvault.BackupMonitor
|
import com.stevesoltys.seedvault.BackupMonitor
|
||||||
import com.stevesoltys.seedvault.BackupNotificationManager
|
import com.stevesoltys.seedvault.BackupNotificationManager
|
||||||
import com.stevesoltys.seedvault.NotificationBackupObserver
|
import com.stevesoltys.seedvault.NotificationBackupObserver
|
||||||
import com.stevesoltys.seedvault.R
|
|
||||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||||
import org.koin.core.context.GlobalContext.get
|
import org.koin.core.context.GlobalContext.get
|
||||||
|
|
||||||
|
@ -52,19 +51,16 @@ class ConfigurableBackupTransportService : Service() {
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun requestBackup(context: Context) {
|
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 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 packages = packageService.eligiblePackages
|
||||||
|
|
||||||
|
val observer = NotificationBackupObserver(context, packages.size, true)
|
||||||
val result = try {
|
val result = try {
|
||||||
val backupManager: IBackupManager = get().koin.get()
|
val backupManager: IBackupManager = get().koin.get()
|
||||||
backupManager.requestBackup(packages, observer, BackupMonitor(), flags)
|
backupManager.requestBackup(packages, observer, BackupMonitor(), FLAG_USER_INITIATED)
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
Log.e(TAG, "Error during backup: ", e)
|
Log.e(TAG, "Error during backup: ", e)
|
||||||
|
val nm: BackupNotificationManager = get().koin.get()
|
||||||
nm.onBackupError()
|
nm.onBackupError()
|
||||||
}
|
}
|
||||||
if (result == BackupManager.SUCCESS) {
|
if (result == BackupManager.SUCCESS) {
|
||||||
|
|
|
@ -70,11 +70,14 @@
|
||||||
<!-- Notification -->
|
<!-- Notification -->
|
||||||
<string name="notification_channel_title">Backup Notification</string>
|
<string name="notification_channel_title">Backup Notification</string>
|
||||||
<string name="notification_title">Backup running</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_complete">Backup complete</string>
|
||||||
<string name="notification_backup_result_rejected">Not backed up</string>
|
<string name="notification_backup_result_rejected">Not backed up</string>
|
||||||
<string name="notification_backup_result_error">Backup failed</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_channel_title">Error Notification</string>
|
||||||
<string name="notification_error_title">Backup Error</string>
|
<string name="notification_error_title">Backup Error</string>
|
||||||
<string name="notification_error_text">A device backup failed to run.</string>
|
<string name="notification_error_text">A device backup failed to run.</string>
|
||||||
|
|
Loading…
Reference in a new issue