fix: keep the built-in timer presets when a backup has none
A backup without the timer_presets field decoded it as an empty list, which means "the user removed them all", so restoring it wiped the built-in presets. A missing field now keeps them. BACKUP.md describes this and the per-alarm clamps.
This commit is contained in:
1 parent
805da39074
commit
3003f75333
4 files changed
+27
-3
No files matched your search
@@ -189,7 +189,7 @@ object BackupCodec {
|
||||
val settingsDto = settings
|
||||
if ((settingsDto.alarmRingtoneUri?.length ?: 0) > MAX_URI_CHARS ||
|
||||
(settingsDto.timerRingtoneUri?.length ?: 0) > MAX_URI_CHARS ||
|
||||
settingsDto.timerPresets.size > 1_000
|
||||
(settingsDto.timerPresets?.size ?: 0) > 1_000
|
||||
) {
|
||||
return BackupResult.Failure("settings contain oversized values")
|
||||
}
|
||||
@@ -206,7 +206,7 @@ object BackupCodec {
|
||||
dismissChallenge = settingsDto.dismissChallenge.toEnum(DismissChallenge.NONE),
|
||||
homeZoneId = settingsDto.homeZoneId?.let(Zones::normalise),
|
||||
),
|
||||
timerPresets = settingsDto.timerPresets.map { it.milliseconds },
|
||||
timerPresets = settingsDto.timerPresets?.map { it.milliseconds } ?: TimerPresets.BUILT_IN,
|
||||
)
|
||||
|
||||
return BackupResult.Success(
|
||||
|
||||
@@ -71,5 +71,10 @@ data class SettingsDto(
|
||||
@SerialName("default_timer_ringtone_uri") val timerRingtoneUri: String? = null,
|
||||
@SerialName("default_dismiss_challenge") val dismissChallenge: String = "NONE",
|
||||
@SerialName("home_zone_id") val homeZoneId: String? = null,
|
||||
@SerialName("timer_presets") val timerPresets: List<Long> = emptyList(),
|
||||
/**
|
||||
* Absent is not empty: an empty list is a user who removed every preset,
|
||||
* while a file without the field (an older or hand-written one) keeps the
|
||||
* built-in ones.
|
||||
*/
|
||||
@SerialName("timer_presets") val timerPresets: List<Long>? = null,
|
||||
)
|
||||
@@ -257,6 +257,19 @@ class BackupCodecTest {
|
||||
assertThat(timers.map { it.duration }).containsExactly(TimerPresets.MIN, TimerPresets.MAX).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a file without timer presets keeps the built-in ones`() {
|
||||
val settings = (BackupCodec.decode(minimalDocument()) as BackupResult.Success).content.settings
|
||||
assertThat(settings.timerPresets).isEqualTo(TimerPresets.BUILT_IN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an empty preset list is honoured as all removed`() {
|
||||
val json = minimalDocument().replace("\"settings\": {}", "\"settings\": {\"timer_presets\": []}")
|
||||
val settings = (BackupCodec.decode(json) as BackupResult.Success).content.settings
|
||||
assertThat(settings.timerPresets).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `garbage text fails rather than throwing`() {
|
||||
assertThat(BackupCodec.decode("not json at all")).isInstanceOf(BackupResult.Failure::class.java)
|
||||
|
||||
@@ -38,6 +38,12 @@ written here.
|
||||
`ClockPrefs`/`SettingsPrefs` already apply on every write, so a value from
|
||||
a future, more permissive version degrades to the nearest valid one instead
|
||||
of failing the import.
|
||||
- An alarm's own `snooze_minutes`, `snooze_limit` and `volume_ramp_seconds`
|
||||
are clamped at parse time to the same ranges as the defaults they
|
||||
override, and a timer's `duration_millis` to the setup range (1 second to
|
||||
24 hours). Room has no clamp of its own, so this is where it happens.
|
||||
- A missing `timer_presets` keeps the built-in presets. An empty list is a
|
||||
user who removed them all, and is imported as such.
|
||||
- An enum field with a name this version does not recognise (a `dismiss_
|
||||
challenge` or `theme_mode` from a future version, say) degrades to that
|
||||
field's default, the same forgiving-read posture `AlarmMapper` already uses
|
||||
|
||||
Reference in new issue
Block a user