Properly schedule/cancel backup workers when backup destination changes

When the user changes to USB storage, we need to cancel current schedulings, because the storage is not always available (maybe can use a trigger URI?). And if moving to a non-USB storage, we need to schedule backups again.

Unfortunately, there are two places in the code where we handle storage location changes. Ideally, those get unified at some point.
This commit is contained in:
Torsten Grote 2024-02-22 11:37:16 -03:00
parent e615402458
commit 0c1898c198
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
4 changed files with 57 additions and 13 deletions

View file

@ -23,7 +23,6 @@ import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.permitDiskReads
import com.stevesoltys.seedvault.restore.RestoreActivity
import com.stevesoltys.seedvault.ui.toRelativeTime
import com.stevesoltys.seedvault.worker.AppBackupWorker
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
@ -128,7 +127,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
val disable = !(newValue as Boolean)
// TODO this should really get moved out off the UI layer
if (disable) {
viewModel.cancelBackupWorkers()
viewModel.cancelFilesBackup()
return@OnPreferenceChangeListener true
}
onEnablingStorageBackup()
@ -215,10 +214,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
return try {
backupManager.isBackupEnabled = enabled
if (enabled) {
AppBackupWorker.schedule(requireContext())
viewModel.scheduleAppBackup()
viewModel.enableCallLogBackup()
} else {
AppBackupWorker.unschedule(requireContext())
viewModel.cancelAppBackup()
}
backup.isChecked = enabled
true
@ -280,7 +279,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
Long.MAX_VALUE -> {
val text = if (backupManager.isBackupEnabled) {
val text = if (backupManager.isBackupEnabled && storage?.isUsb != true) {
getString(R.string.notification_title)
} else {
getString(R.string.settings_backup_last_backup_never)
@ -315,7 +314,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
LENGTH_LONG
).show()
}
viewModel.scheduleBackupWorkers()
viewModel.scheduleFilesBackup()
backupStorage.isChecked = true
dialog.dismiss()
}

View file

@ -129,11 +129,13 @@ internal class SettingsViewModel(
Log.i(TAG, "onStorageLocationChanged")
if (storage.isUsb) {
// disable storage backup if new storage is on USB
cancelBackupWorkers()
cancelAppBackup()
cancelFilesBackup()
} else {
// enable it, just in case the previous storage was on USB,
// also to update the network requirement of the new storage
scheduleBackupWorkers()
scheduleAppBackup()
scheduleFilesBackup()
}
onStoragePropertiesChanged()
}
@ -245,11 +247,15 @@ internal class SettingsViewModel(
return keyManager.hasMainKey()
}
fun scheduleBackupWorkers() {
fun scheduleAppBackup() {
val storage = settingsManager.getStorage() ?: error("no storage available")
if (!storage.isUsb) {
if (backupManager.isBackupEnabled) AppBackupWorker.schedule(app)
if (settingsManager.isStorageBackupEnabled()) BackupJobService.scheduleJob(
if (!storage.isUsb && backupManager.isBackupEnabled) AppBackupWorker.schedule(app)
}
fun scheduleFilesBackup() {
val storage = settingsManager.getStorage() ?: error("no storage available")
if (!storage.isUsb && settingsManager.isStorageBackupEnabled()) {
BackupJobService.scheduleJob(
context = app,
jobServiceClass = StorageBackupJobService::class.java,
periodMillis = HOURS.toMillis(24),
@ -261,8 +267,11 @@ internal class SettingsViewModel(
}
}
fun cancelBackupWorkers() {
fun cancelAppBackup() {
AppBackupWorker.unschedule(app)
}
fun cancelFilesBackup() {
BackupJobService.cancelJob(app)
}

View file

@ -102,6 +102,7 @@ internal class RecoveryCodeViewModel(
*/
fun reinitializeBackupLocation() {
Log.d(TAG, "Re-initializing backup location...")
// TODO this code is almost identical to BackupStorageViewModel#onLocationSet(), unify?
GlobalScope.launch(Dispatchers.IO) {
// remove old storage snapshots and clear cache
storageBackup.deleteAllSnapshots()

View file

@ -4,6 +4,7 @@ import android.app.Application
import android.app.backup.BackupProgress
import android.app.backup.IBackupManager
import android.app.backup.IBackupObserver
import android.app.job.JobInfo
import android.net.Uri
import android.os.UserHandle
import android.util.Log
@ -11,12 +12,15 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.viewModelScope
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.storage.StorageBackupJobService
import com.stevesoltys.seedvault.transport.TRANSPORT_ID
import com.stevesoltys.seedvault.worker.AppBackupWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.calyxos.backup.storage.api.StorageBackup
import org.calyxos.backup.storage.backup.BackupJobService
import java.io.IOException
import java.util.concurrent.TimeUnit
private val TAG = BackupStorageViewModel::class.java.simpleName
@ -31,8 +35,18 @@ internal class BackupStorageViewModel(
override fun onLocationSet(uri: Uri) {
val isUsb = saveStorage(uri)
if (isUsb) {
// disable storage backup if new storage is on USB
cancelBackupWorkers()
} else {
// enable it, just in case the previous storage was on USB,
// also to update the network requirement of the new storage
scheduleBackupWorkers()
}
viewModelScope.launch(Dispatchers.IO) {
// remove old storage snapshots and clear cache
// TODO is this needed? It also does create all 255 chunk folders which takes time
// pass a flag to getCurrentBackupSnapshots() to not create missing folders?
storageBackup.deleteAllSnapshots()
storageBackup.clearCache()
try {
@ -52,6 +66,27 @@ internal class BackupStorageViewModel(
}
}
private fun scheduleBackupWorkers() {
val storage = settingsManager.getStorage() ?: error("no storage available")
if (!storage.isUsb) {
if (backupManager.isBackupEnabled) AppBackupWorker.schedule(app)
if (settingsManager.isStorageBackupEnabled()) BackupJobService.scheduleJob(
context = app,
jobServiceClass = StorageBackupJobService::class.java,
periodMillis = TimeUnit.HOURS.toMillis(24),
networkType = if (storage.requiresNetwork) JobInfo.NETWORK_TYPE_UNMETERED
else JobInfo.NETWORK_TYPE_NONE,
deviceIdle = false,
charging = true
)
}
}
private fun cancelBackupWorkers() {
AppBackupWorker.unschedule(app)
BackupJobService.cancelJob(app)
}
@WorkerThread
private inner class InitializationObserver(val requestBackup: Boolean) :
IBackupObserver.Stub() {