fix(backup): stop orphaned backup work when disabled
Turning automatic backup off only cancelled the periodic work, so a run-now that kept failing (e.g. its folder was deleted) retried forever and spammed failure notifications. - Cancel the immediate "run now" work too when backup is disabled. - Worker no-ops (no retry) when the toggle is off, so already-queued work can't revive itself. - Reconcile scheduled work against saved settings on every app launch — re-arms after a reinstall and clears orphaned work once backup is off. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,16 @@
|
|||||||
package de.jeanlucmakiola.calendula
|
package de.jeanlucmakiola.calendula
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import dagger.hilt.android.EntryPointAccessors
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
import de.jeanlucmakiola.calendula.data.backup.BackupScheduler
|
||||||
|
import de.jeanlucmakiola.calendula.data.backup.BackupWorker
|
||||||
import de.jeanlucmakiola.calendula.data.crash.CrashReporter
|
import de.jeanlucmakiola.calendula.data.crash.CrashReporter
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application entry point. Registered as android:name=".CalendulaApp"
|
* Application entry point. Registered as android:name=".CalendulaApp"
|
||||||
@@ -16,5 +24,24 @@ class CalendulaApp : Application() {
|
|||||||
// Install first thing so startup crashes are captured too (privacy-
|
// Install first thing so startup crashes are captured too (privacy-
|
||||||
// respecting, on-device; the user submits the report by hand).
|
// respecting, on-device; the user submits the report by hand).
|
||||||
CrashReporter.install(this)
|
CrashReporter.install(this)
|
||||||
|
reconcileAutoBackup()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bring the scheduled auto-backup work back in line with the saved settings
|
||||||
|
* on every launch — re-arms it after a reinstall and, crucially, cancels any
|
||||||
|
* orphaned work once backup has been turned off.
|
||||||
|
*/
|
||||||
|
private fun reconcileAutoBackup() {
|
||||||
|
val deps = EntryPointAccessors.fromApplication(this, BackupWorker.Deps::class.java)
|
||||||
|
CoroutineScope(SupervisorJob() + Dispatchers.Default).launch {
|
||||||
|
val prefs = deps.settingsPrefs()
|
||||||
|
BackupScheduler.apply(
|
||||||
|
context = this@CalendulaApp,
|
||||||
|
enabled = prefs.autoBackupEnabled.first(),
|
||||||
|
intervalMinutes = prefs.autoBackupIntervalMinutes.first(),
|
||||||
|
hasFolder = prefs.autoBackupFolderUri.first() != null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ object BackupScheduler {
|
|||||||
val workManager = WorkManager.getInstance(context)
|
val workManager = WorkManager.getInstance(context)
|
||||||
if (!enabled || !hasFolder) {
|
if (!enabled || !hasFolder) {
|
||||||
workManager.cancelUniqueWork(WORK_NAME)
|
workManager.cancelUniqueWork(WORK_NAME)
|
||||||
|
// Also cancel any pending immediate run — otherwise a failing run-now
|
||||||
|
// keeps retrying after the user has turned backup off.
|
||||||
|
workManager.cancelUniqueWork(WORK_NAME_NOW)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// WorkManager's own floor is 15 min; the UI floors the user choice at 30.
|
// WorkManager's own floor is 15 min; the UI floors the user choice at 30.
|
||||||
@@ -106,6 +109,9 @@ class BackupWorker(
|
|||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val deps = EntryPointAccessors.fromApplication(applicationContext, Deps::class.java)
|
val deps = EntryPointAccessors.fromApplication(applicationContext, Deps::class.java)
|
||||||
val prefs = deps.settingsPrefs()
|
val prefs = deps.settingsPrefs()
|
||||||
|
// Respect the toggle even for already-queued work: if backup was turned
|
||||||
|
// off, no-op (and don't retry) so a lingering run can't revive itself.
|
||||||
|
if (!prefs.autoBackupEnabled.first()) return Result.success()
|
||||||
val folder = prefs.autoBackupFolderUri.first()
|
val folder = prefs.autoBackupFolderUri.first()
|
||||||
?: return Result.failure() // nothing to write to — leave scheduling to settings
|
?: return Result.failure() // nothing to write to — leave scheduling to settings
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
|
|||||||
Reference in New Issue
Block a user