Count passphrase-only Google backup as 'configured' in the UI

Split the overloaded isBackupConfigured into isS3Configured (gates the
S3 upload path — unchanged behaviour) and isAnyBackupConfigured (S3 OR
a passphrase, which drives the always-on Google blob). The settings
status card and the Today nudge now use the latter, so a passphrase-only
setup reads as protected instead of 'ikke satt opp'. Status line
distinguishes the three states: Google-only, Google+S3, and nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-06-11 21:24:22 +02:00
commit 0cae8a8ef9
5 changed files with 25 additions and 14 deletions

View file

@ -116,11 +116,11 @@ class BackupManager(private val context: Context) {
suspend fun runConfiguredBackups(): BackupOutcome {
val settings = SettingsStore(context)
val hasPassphrase = !settings.autoBackupPassphrase.isNullOrEmpty()
require(hasPassphrase || settings.isBackupConfigured) { "Ingen sikkerhetskopi er satt opp" }
require(settings.isAnyBackupConfigured) { "Ingen sikkerhetskopi er satt opp" }
// Google Auto Backup blob: written whenever a passphrase exists.
writeCloudBlob()
val s3Key = if (settings.isBackupConfigured) runAutoBackup() else null
val s3Key = if (settings.isS3Configured) runAutoBackup() else null
return BackupOutcome(cloudBlobWritten = hasPassphrase, s3Key = s3Key)
}

View file

@ -28,7 +28,7 @@ class BackupWorker(context: Context, params: WorkerParameters) : CoroutineWorker
} catch (e: Exception) {
return if (runAttemptCount < 3) Result.retry() else Result.failure()
}
if (!SettingsStore(applicationContext).isBackupConfigured) return Result.success()
if (!SettingsStore(applicationContext).isS3Configured) return Result.success()
return try {
manager.runAutoBackup()
Result.success()

View file

@ -55,12 +55,21 @@ class SettingsStore(context: Context) {
get() = prefs.getInt("supply_check_minute", 600)
set(v) = prefs.edit { putInt("supply_check_minute", v.coerceIn(0, 1439)) }
val isBackupConfigured: Boolean
/** S3/Garage credentials complete — gates the S3 upload path only. */
val isS3Configured: Boolean
get() = !s3Endpoint.isNullOrBlank() && !s3Bucket.isNullOrBlank() &&
!s3AccessKey.isNullOrBlank() && !s3SecretKey.isNullOrBlank()
/**
* Any backup mechanism active: S3, OR a passphrase alone (which drives the
* always-on Google Auto Backup blob). What the UI "configured?" status and
* the Today nudge key off so a passphrase-only setup reads as protected.
*/
val isAnyBackupConfigured: Boolean
get() = isS3Configured || !autoBackupPassphrase.isNullOrEmpty()
fun s3Client(): no.naiv.meddetsamme.backup.S3Client? {
if (!isBackupConfigured) return null
if (!isS3Configured) return null
return no.naiv.meddetsamme.backup.S3Client(
endpoint = s3Endpoint!!.trimEnd('/'),
region = s3Region?.takeIf { it.isNotBlank() } ?: "garage",

View file

@ -80,13 +80,19 @@ fun SettingsScreen(nav: Nav) {
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
// Backup got its own page — seven fields drowned everything else here.
val configured = settings.isAnyBackupConfigured
val statusLine = when {
settings.isS3Configured -> "Google + S3/Garage konfigurert"
configured -> "Google-sikkerhetskopi aktiv (kryptert)"
else -> "Ikke satt opp — helsedata finnes bare på denne enheten"
}
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { nav.push(Screen.BackupSettings) }
.semantics(mergeDescendants = true) {
contentDescription = if (settings.isBackupConfigured) {
"Sikkerhetskopi, konfigurert. Trykk for å åpne."
contentDescription = if (configured) {
"Sikkerhetskopi: $statusLine. Trykk for å åpne."
} else {
"Sikkerhetskopi, ikke satt opp. Trykk for å sette opp."
}
@ -96,13 +102,9 @@ fun SettingsScreen(nav: Nav) {
Column(Modifier.weight(1f)) {
Text("Sikkerhetskopi", style = MaterialTheme.typography.titleMedium)
Text(
if (settings.isBackupConfigured) {
"S3/Garage konfigurert · eksport og import"
} else {
"Ikke satt opp — helsedata finnes bare på denne enheten"
},
statusLine,
style = MaterialTheme.typography.bodyMedium,
color = if (settings.isBackupConfigured) {
color = if (configured) {
MaterialTheme.colorScheme.onSurfaceVariant
} else {
MaterialTheme.colorScheme.error

View file

@ -382,7 +382,7 @@ private fun BackupNudge(onSetup: () -> Unit) {
val context = LocalContext.current
val settings = remember { SettingsStore(context) }
var show by remember {
mutableStateOf(!settings.isBackupConfigured && !settings.backupNudgeDismissed)
mutableStateOf(!settings.isAnyBackupConfigured && !settings.backupNudgeDismissed)
}
if (!show) return
Card(modifier = Modifier.fillMaxWidth()) {