From 9839520b7be475c05a2444db047e824a4d737afb Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 22 Oct 2026 08:20:19 +0200 Subject: [PATCH] refactor(alarms): one date formatter, and a picker year range sized to its date The list and editor now format a date through the same helper as the widget, and the date pickers keep their default years unless a stored date lies beyond them, instead of always spanning to 9999. --- .../clockula/ui/alarms/AlarmDatePickers.kt | 21 ++++++++++++------- docs/ARCHITECTURE.md | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/de/jeanlucmakiola/clockula/ui/alarms/AlarmDatePickers.kt b/app/src/main/java/de/jeanlucmakiola/clockula/ui/alarms/AlarmDatePickers.kt index 7187fd2..6123d4f 100644 --- a/app/src/main/java/de/jeanlucmakiola/clockula/ui/alarms/AlarmDatePickers.kt +++ b/app/src/main/java/de/jeanlucmakiola/clockula/ui/alarms/AlarmDatePickers.kt @@ -5,6 +5,7 @@ import android.text.format.DateUtils import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDefaults import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.DateRangePicker import androidx.compose.material3.ExperimentalMaterial3Api @@ -51,11 +52,12 @@ internal object PickerDates { } /** - * Up to [AlarmDates.MAX], not the picker's default 2100: an imported date - * past the default would be an initial selection outside the range, - * which the picker state refuses by throwing. + * The picker's default years, stretched to [selected] when an imported + * date lies beyond them: an initial selection outside the range is + * refused by the picker state with a throw. */ - fun yearRange(earliest: LocalDate): IntRange = earliest.year..AlarmDates.MAX.year + fun yearRange(earliest: LocalDate, selected: LocalDate? = null): IntRange = + earliest.year..maxOf(DatePickerDefaults.YearRange.last, selected?.year ?: earliest.year) } /** @@ -77,7 +79,7 @@ internal fun ScheduleDatePickerDialog( val state = rememberDatePickerState( initialSelectedDateMillis = start?.let(PickerDates::toMillis), initialDisplayedMonthMillis = PickerDates.toMillis(start ?: earliest), - yearRange = PickerDates.yearRange(earliest), + yearRange = PickerDates.yearRange(earliest, start), selectableDates = selectable, ) val picked = state.selectedDateMillis @@ -126,7 +128,7 @@ internal fun PauseRangePickerDialog( initialSelectedStartDateMillis = start?.let(PickerDates::toMillis), initialSelectedEndDateMillis = end?.let(PickerDates::toMillis), initialDisplayedMonthMillis = PickerDates.toMillis(maxOf(start ?: today, today)), - yearRange = PickerDates.yearRange(earliest), + yearRange = PickerDates.yearRange(earliest, end), selectableDates = selectable, ) val pickedStart = state.selectedStartDateMillis @@ -174,8 +176,11 @@ private fun DismissButtons(onRemove: (() -> Unit)?, onDismiss: () -> Unit) { /** "Wed, Oct 28" in the reader's locale; the year only when it is not this one. */ @Composable -internal fun scheduledDateText(date: LocalDate): String = - formatDates(date, date, DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_ABBREV_WEEKDAY) +internal fun scheduledDateText(date: LocalDate): String { + val context = LocalContext.current + val configuration = LocalConfiguration.current + return remember(date, configuration) { AlarmDateText.weekdayDate(context, Locale.getDefault(), date) } +} /** "Oct 26 – 28", "Oct 30 – Nov 2" or "Oct 26": the platform's own localised range. */ @Composable diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 3a3260a..6e1667f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1345,7 +1345,7 @@ on a date set for today drops the date. Picking a date switches the alarm on, si a switched-off alarm would read "Scheduled for" and stay silent. Picking a repeat day clears the date and clearing the last one clears the pause, as in Google Clock. The `AlarmClock` contract cannot express either field, so `SET_ALARM` never -re-uses an alarm that carries one (§17). +re-uses an alarm whose date or pause is still in force (§17). ## 12. The app shell