Show notification for backup running in the background
The system triggers backup jobs periodically or when a package is announcing that its data has changed. So far we were not showing notifications for those. This commit shows a notification with an indeterminate progress bar as we don't have any information about how many packages will get backed up.
This commit is contained in:
parent
72871d3d66
commit
b9ffe2c03e
3 changed files with 56 additions and 44 deletions
|
@ -13,10 +13,12 @@ import android.os.RemoteException
|
|||
import android.util.Log
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.stevesoltys.seedvault.BackupMonitor
|
||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import com.stevesoltys.seedvault.ui.notification.NotificationBackupObserver
|
||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.context.GlobalContext.get
|
||||
import org.koin.core.inject
|
||||
|
||||
private val TAG = ConfigurableBackupTransportService::class.java.simpleName
|
||||
|
||||
|
@ -24,10 +26,12 @@ private val TAG = ConfigurableBackupTransportService::class.java.simpleName
|
|||
* @author Steve Soltys
|
||||
* @author Torsten Grote
|
||||
*/
|
||||
class ConfigurableBackupTransportService : Service() {
|
||||
class ConfigurableBackupTransportService : Service(), KoinComponent {
|
||||
|
||||
private var transport: ConfigurableBackupTransport? = null
|
||||
|
||||
private val notificationManager: BackupNotificationManager by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
transport = ConfigurableBackupTransport(applicationContext)
|
||||
|
@ -43,6 +47,7 @@ class ConfigurableBackupTransportService : Service() {
|
|||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
notificationManager.onBackupBackgroundFinished()
|
||||
transport = null
|
||||
Log.d(TAG, "Service destroyed.")
|
||||
}
|
||||
|
@ -55,7 +60,7 @@ fun requestBackup(context: Context) {
|
|||
val packages = packageService.eligiblePackages
|
||||
val appTotals = packageService.expectedAppTotals
|
||||
|
||||
val observer = NotificationBackupObserver(context, packages.size, appTotals, true)
|
||||
val observer = NotificationBackupObserver(context, packages.size, appTotals)
|
||||
val result = try {
|
||||
val backupManager: IBackupManager = get().koin.get()
|
||||
backupManager.requestBackup(packages, observer, BackupMonitor(), FLAG_USER_INITIATED)
|
||||
|
|
|
@ -31,6 +31,7 @@ private const val CHANNEL_ID_RESTORE_ERROR = "NotificationRestoreError"
|
|||
private const val NOTIFICATION_ID_OBSERVER = 1
|
||||
private const val NOTIFICATION_ID_ERROR = 2
|
||||
private const val NOTIFICATION_ID_RESTORE_ERROR = 3
|
||||
private const val NOTIFICATION_ID_BACKGROUND = 4
|
||||
|
||||
private val TAG = BackupNotificationManager::class.java.simpleName
|
||||
|
||||
|
@ -71,14 +72,12 @@ internal class BackupNotificationManager(private val context: Context) {
|
|||
*/
|
||||
fun onBackupStarted(
|
||||
expectedPackages: Int,
|
||||
appTotals: ExpectedAppTotals,
|
||||
userInitiated: Boolean
|
||||
appTotals: ExpectedAppTotals
|
||||
) {
|
||||
updateBackupNotification(
|
||||
infoText = "", // This passes quickly, no need to show something here
|
||||
transferred = 0,
|
||||
expected = expectedPackages,
|
||||
userInitiated = userInitiated
|
||||
expected = expectedPackages
|
||||
)
|
||||
expectedApps = expectedPackages
|
||||
expectedOptOutApps = appTotals.appsOptOut
|
||||
|
@ -89,57 +88,55 @@ internal class BackupNotificationManager(private val context: Context) {
|
|||
* This is expected to get called before [onOptOutAppBackup] and [onBackupUpdate].
|
||||
*/
|
||||
fun onPmKvBackup(packageName: String, transferred: Int, expected: Int) {
|
||||
val text = "@pm@ record for $packageName"
|
||||
if (expectedApps == null) {
|
||||
Log.d(TAG, "Expected number of apps unknown. Not showing @pm@ notification.")
|
||||
return
|
||||
updateBackgroundBackupNotification(text)
|
||||
} else {
|
||||
val addend = (expectedOptOutApps ?: 0) + (expectedApps ?: 0)
|
||||
updateBackupNotification(
|
||||
infoText = text,
|
||||
transferred = transferred,
|
||||
expected = expected + addend
|
||||
)
|
||||
expectedPmRecords = expected
|
||||
}
|
||||
val addend = (expectedOptOutApps ?: 0) + (expectedApps ?: 0)
|
||||
updateBackupNotification(
|
||||
infoText = "@pm@ record for $packageName",
|
||||
transferred = transferred,
|
||||
expected = expected + addend,
|
||||
userInitiated = false
|
||||
)
|
||||
expectedPmRecords = expected
|
||||
}
|
||||
|
||||
/**
|
||||
* This should get called after [onPmKvBackup], but before [onBackupUpdate].
|
||||
*/
|
||||
fun onOptOutAppBackup(packageName: String, transferred: Int, expected: Int) {
|
||||
val text = "Opt-out APK for $packageName"
|
||||
if (expectedApps == null) {
|
||||
Log.d(TAG, "Expected number of apps unknown. Not showing APK notification.")
|
||||
return
|
||||
updateBackgroundBackupNotification(text)
|
||||
} else {
|
||||
updateBackupNotification(
|
||||
infoText = text,
|
||||
transferred = transferred + (expectedPmRecords ?: 0),
|
||||
expected = expected + (expectedApps ?: 0) + (expectedPmRecords ?: 0)
|
||||
)
|
||||
expectedOptOutApps = expected
|
||||
}
|
||||
updateBackupNotification(
|
||||
infoText = "Opt-out APK for $packageName",
|
||||
transferred = transferred + (expectedPmRecords ?: 0),
|
||||
expected = expected + (expectedApps ?: 0) + (expectedPmRecords ?: 0),
|
||||
userInitiated = false
|
||||
)
|
||||
expectedOptOutApps = expected
|
||||
}
|
||||
|
||||
/**
|
||||
* In the series of notification updates,
|
||||
* this type is is expected to get called after [onOptOutAppBackup] and [onPmKvBackup].
|
||||
*/
|
||||
fun onBackupUpdate(app: CharSequence, transferred: Int, userInitiated: Boolean) {
|
||||
fun onBackupUpdate(app: CharSequence, transferred: Int) {
|
||||
val expected = expectedApps ?: error("expectedApps is null")
|
||||
val addend = (expectedOptOutApps ?: 0) + (expectedPmRecords ?: 0)
|
||||
updateBackupNotification(
|
||||
infoText = app,
|
||||
transferred = transferred + addend,
|
||||
expected = expected + addend,
|
||||
userInitiated = userInitiated
|
||||
expected = expected + addend
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateBackupNotification(
|
||||
infoText: CharSequence,
|
||||
transferred: Int,
|
||||
expected: Int,
|
||||
userInitiated: Boolean
|
||||
expected: Int
|
||||
) {
|
||||
@Suppress("MagicNumber")
|
||||
val percentage = (transferred.toFloat() / expected) * 100
|
||||
|
@ -149,22 +146,33 @@ internal class BackupNotificationManager(private val context: Context) {
|
|||
setSmallIcon(R.drawable.ic_cloud_upload)
|
||||
setContentTitle(context.getString(R.string.notification_title))
|
||||
setContentText(percentageStr)
|
||||
setTicker(infoText)
|
||||
setOngoing(true)
|
||||
setShowWhen(false)
|
||||
setWhen(System.currentTimeMillis())
|
||||
setProgress(expected, transferred, false)
|
||||
priority = if (userInitiated) PRIORITY_DEFAULT else PRIORITY_LOW
|
||||
priority = PRIORITY_DEFAULT
|
||||
}.build()
|
||||
nm.notify(NOTIFICATION_ID_OBSERVER, notification)
|
||||
}
|
||||
|
||||
fun onBackupFinished(success: Boolean, numBackedUp: Int?, userInitiated: Boolean) {
|
||||
if (!userInitiated) {
|
||||
// don't show permanent finished notification if backup was not triggered by user
|
||||
nm.cancel(NOTIFICATION_ID_OBSERVER)
|
||||
return
|
||||
}
|
||||
private fun updateBackgroundBackupNotification(infoText: CharSequence) {
|
||||
Log.i(TAG, "$infoText")
|
||||
val notification = Builder(context, CHANNEL_ID_OBSERVER).apply {
|
||||
setSmallIcon(R.drawable.ic_cloud_upload)
|
||||
setContentTitle(context.getString(R.string.notification_title))
|
||||
setShowWhen(false)
|
||||
setWhen(System.currentTimeMillis())
|
||||
setProgress(0, 0, true)
|
||||
priority = PRIORITY_LOW
|
||||
}.build()
|
||||
nm.notify(NOTIFICATION_ID_BACKGROUND, notification)
|
||||
}
|
||||
|
||||
fun onBackupBackgroundFinished() {
|
||||
nm.cancel(NOTIFICATION_ID_BACKGROUND)
|
||||
}
|
||||
|
||||
fun onBackupFinished(success: Boolean, numBackedUp: Int?) {
|
||||
val titleRes =
|
||||
if (success) R.string.notification_success_title else R.string.notification_failed_title
|
||||
val total = expectedAppTotals?.appsTotal
|
||||
|
|
|
@ -19,8 +19,7 @@ private val TAG = NotificationBackupObserver::class.java.simpleName
|
|||
internal class NotificationBackupObserver(
|
||||
private val context: Context,
|
||||
private val expectedPackages: Int,
|
||||
appTotals: ExpectedAppTotals,
|
||||
private val userInitiated: Boolean
|
||||
appTotals: ExpectedAppTotals
|
||||
) : IBackupObserver.Stub(), KoinComponent {
|
||||
|
||||
private val nm: BackupNotificationManager by inject()
|
||||
|
@ -31,7 +30,7 @@ internal class NotificationBackupObserver(
|
|||
init {
|
||||
// Inform the notification manager that a backup has started
|
||||
// and inform about the expected numbers, so it can compute a total.
|
||||
nm.onBackupStarted(expectedPackages, appTotals, userInitiated)
|
||||
nm.onBackupStarted(expectedPackages, appTotals)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +78,7 @@ internal class NotificationBackupObserver(
|
|||
}
|
||||
val success = status == 0
|
||||
val numBackedUp = if (success) metadataManager.getPackagesNumBackedUp() else null
|
||||
nm.onBackupFinished(success, numBackedUp, userInitiated)
|
||||
nm.onBackupFinished(success, numBackedUp)
|
||||
}
|
||||
|
||||
private fun showProgressNotification(packageName: String) {
|
||||
|
@ -93,7 +92,7 @@ internal class NotificationBackupObserver(
|
|||
currentPackage = packageName
|
||||
val app = getAppName(packageName)
|
||||
numPackages += 1
|
||||
nm.onBackupUpdate(app, numPackages, userInitiated)
|
||||
nm.onBackupUpdate(app, numPackages)
|
||||
}
|
||||
|
||||
private fun getAppName(packageId: String): CharSequence = getAppName(context, packageId)
|
||||
|
|
Loading…
Reference in a new issue