fix(alarms): word the alarm-set snackbar once per announcement
Every write re-read the engine and re-worded a standing announcement, so tapping Mon-Fri while the snackbar showed restarted it on each tap, as did a label keystroke across a minute. The words now come from the first read; later writes can only take the snackbar down, when the alarm stops ringing. The settled announcement still replaces it once. Deleting no longer retracts by hand: a deleted alarm already reads as nothing to say. Refs #19
This commit is contained in:
1 parent
d2e73901c1
commit
706a384704
3 files changed
+43
-12
No files matched your search
@@ -248,8 +248,6 @@ class AlarmEditorViewModel @Inject internal constructor(
|
||||
if (engine.ringSession()?.alarm?.id == alarmId) engine.dismiss(alarmId)
|
||||
alarms.delete(alarmId)
|
||||
engine.reschedule()
|
||||
// "Set for 2 hours from now" must not outlive the alarm on the list.
|
||||
announcer.retract(alarmId)
|
||||
_state.value = AlarmEditorUiState.Gone
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +99,24 @@ internal class AlarmSetAnnouncer @Inject constructor(
|
||||
if (announcement == null || elapsed.elapsedRealtime() - announcement.at >= FRESH_FOR) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
// Worded once, from the first read: later writes — the next
|
||||
// repeat-day tap, a label keystroke across a minute — must not
|
||||
// restart a snackbar already showing. They can only take it
|
||||
// down, when the alarm stops ringing at all.
|
||||
var worded: AlarmSetNotice? = null
|
||||
engine.upcoming().map { upcoming ->
|
||||
val entry = upcoming.firstOrNull { it.alarm.id == announcement.alarmId }
|
||||
val countdown = AlarmSetCountdown.of(entry, wall.now())
|
||||
// Nothing to say is the answer for good: an announcement
|
||||
// left standing would speak up later, long after the
|
||||
// action, should something else switch the alarm back on.
|
||||
if (countdown == null) dropIfCurrent(announcement)
|
||||
countdown?.let { AlarmSetNotice(announcement.alarmId, it, announcement.sequence) }
|
||||
if (countdown == null) {
|
||||
// Nothing to say is the answer for good: left standing,
|
||||
// it would speak up long after the action, should
|
||||
// something else switch the alarm back on.
|
||||
clear(announcement.sequence)
|
||||
null
|
||||
} else {
|
||||
worded ?: AlarmSetNotice(announcement.alarmId, countdown, announcement.sequence)
|
||||
.also { worded = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,12 +160,10 @@ internal class AlarmSetAnnouncer @Inject constructor(
|
||||
}
|
||||
|
||||
/** [notice] was shown until it timed out or was swiped away. */
|
||||
fun consumed(notice: AlarmSetNotice) {
|
||||
current.update { standing -> standing?.takeUnless { it.sequence == notice.sequence } }
|
||||
}
|
||||
fun consumed(notice: AlarmSetNotice) = clear(notice.sequence)
|
||||
|
||||
private fun dropIfCurrent(announcement: Announcement) {
|
||||
current.update { standing -> standing?.takeUnless { it.sequence == announcement.sequence } }
|
||||
private fun clear(sequence: Long) {
|
||||
current.update { standing -> standing?.takeUnless { it.sequence == sequence } }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -343,6 +343,31 @@ class AlarmEditorViewModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `day taps while a notice shows do not restart it before they settle`(@TempDir tempDir: Path) =
|
||||
runTest(main.dispatcher) {
|
||||
val harness = alarmEngineHarness(tempDir, now = fiveToday)
|
||||
harness.givenAlarmWith(hour = 7)
|
||||
val viewModel = editor(harness)
|
||||
advanceUntilIdle()
|
||||
viewModel.onTimePicked(9, 0)
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.alarmSetNotice.test {
|
||||
assertThat(awaitItem()?.countdown).isEqualTo(NextFireLabel.InHoursMinutes(4, 0))
|
||||
viewModel.onDayToggled(DayOfWeek.MONDAY)
|
||||
advanceTimeBy(300)
|
||||
viewModel.onDayToggled(DayOfWeek.TUESDAY)
|
||||
advanceTimeBy(300)
|
||||
expectNoEvents()
|
||||
advanceUntilIdle()
|
||||
|
||||
// One more snackbar, once settled: Wednesday 05:00 to Monday 09:00.
|
||||
assertThat(awaitItem()?.countdown).isEqualTo(NextFireLabel.InDaysHours(5, 4))
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `leaving while the days settle still announces them, for the list`(@TempDir tempDir: Path) =
|
||||
runTest(main.dispatcher) {
|
||||
|
||||
Reference in new issue
Block a user