Skip to content

Commit

Permalink
Properly schedule/cancel backup workers when backup destination changes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
grote committed Feb 22, 2024
1 parent bfdc267 commit ea0ca58
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,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()
Expand Down Expand Up @@ -215,10 +215,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
Expand Down Expand Up @@ -311,7 +311,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
Toast.makeText(context, R.string.settings_backup_storage_battery_optimization,
LENGTH_LONG).show()
}
viewModel.scheduleBackupWorkers()
viewModel.scheduleFilesBackup()
backupStorage.isChecked = true
dialog.dismiss()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,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()
}
Expand Down Expand Up @@ -244,11 +246,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),
Expand All @@ -260,8 +266,11 @@ internal class SettingsViewModel(
}
}

fun cancelBackupWorkers() {
fun cancelAppBackup() {
AppBackupWorker.unschedule(app)
}

fun cancelFilesBackup() {
BackupJobService.cancelJob(app)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ 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
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

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

0 comments on commit ea0ca58

Please sign in to comment.