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.
This commit is contained in:
makiolaj committed 2026-10-22 08:20:19 +02:00
1 parent c5de9becdf
commit 9839520b7b
2 files changed
+14 -9

No files matched your search

@@ -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
+1 -1
View File
@@ -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