Make the clock agenda's calendars and row count configurable

The agenda part filtered calendars through the search settings and always
collapsed after four rows. It now has its own selection: an exclusion set
(`clockWidgetCalendarPartExcludedCalendars`, empty = all) that is added to the
search settings' exclusions, because the picker only offers calendars those
settings leave enabled.

The row cap is `clockWidgetCalendarPartMaxRows` (1..8, default 4); the agenda
shows that many events and then a "+N more events" row if more are left, so
with the default five events now render as four rows plus "+1 more event"
instead of three plus "+2".

Both are configured in `ConfigureCalendarPart`, shown by the clock sheet while
the Events part is enabled: a slider, a Calendars row that expands into a
colour-tinted checkbox per calendar, and a button to Settings -> Search ->
Calendar (new `ROUTE_SEARCH_CALENDARS`) for calendars the search settings
disable. A missing calendar permission shows a banner with a Grant button.

New setting fields default to the previous behaviour, so no datastore
migration is needed.
This commit is contained in:
2026-09-17 08:56:55 +02:00
parent c56ea58fc5
commit c824b100b6
9 changed files with 321 additions and 9 deletions

View File

@@ -615,6 +615,12 @@
<string name="preference_clockwidget_alarm_part_summary">Show alarms that will ring within the next 24 hours</string>
<string name="preference_clockwidget_calendar_part">Events</string>
<string name="preference_clockwidget_calendar_part_summary">Show today\'s agenda</string>
<string name="preference_clockwidget_calendar_part_events">Visible events</string>
<string name="preference_clockwidget_calendar_part_calendars">Calendars</string>
<string name="preference_clockwidget_calendar_part_all_calendars">All calendars</string>
<string name="preference_clockwidget_calendar_part_calendars_summary">%1$d of %2$d calendars</string>
<string name="preference_clockwidget_calendar_part_no_calendars">No calendars found</string>
<string name="preference_clockwidget_calendar_part_manage_calendars">Manage calendars</string>
<string name="clockwidget_calendar_part_all_day">all-day</string>
<plurals name="clockwidget_calendar_part_more_events">
<item quantity="one">+%d more event</item>

View File

@@ -59,6 +59,8 @@ data class LauncherSettingsData internal constructor(
val clockWidgetMusicPart: Boolean = true,
val clockWidgetDatePart: Boolean = true,
val clockWidgetCalendarPart: Boolean = false,
val clockWidgetCalendarPartExcludedCalendars: Set<String> = emptySet(),
val clockWidgetCalendarPartMaxRows: Int = 4,
val clockWidgetDynamicZoneSlots: Int = 1,
val clockWidgetFillHeight: Boolean = false,
val clockWidgetAlignment: ClockWidgetAlignment = ClockWidgetAlignment.Bottom,

View File

@@ -11,6 +11,10 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** Bounds for the number of event rows the agenda shows before collapsing. */
const val MinCalendarPartRows = 1
const val MaxCalendarPartRows = 8
data class ClockWidgetParts(
val date: Boolean,
val music: Boolean = false,
@@ -61,6 +65,39 @@ class ClockWidgetSettings internal constructor(
}
}
/**
* Calendars the agenda does not show. Empty means "all calendars".
*/
val calendarPartExcludedCalendars
get() = launcherDataStore.data.map { it.clockWidgetCalendarPartExcludedCalendars }
.distinctUntilChanged()
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
launcherDataStore.update {
it.copy(
clockWidgetCalendarPartExcludedCalendars =
if (excluded) it.clockWidgetCalendarPartExcludedCalendars + calendarId
else it.clockWidgetCalendarPartExcludedCalendars - calendarId
)
}
}
/**
* Number of event rows in the agenda before it collapses into a "+N more events" row.
*/
val calendarPartMaxRows
get() = launcherDataStore.data
.map { it.clockWidgetCalendarPartMaxRows.coerceIn(MinCalendarPartRows, MaxCalendarPartRows) }
.distinctUntilChanged()
fun setCalendarPartMaxRows(rows: Int) {
launcherDataStore.update {
it.copy(
clockWidgetCalendarPartMaxRows = rows.coerceIn(MinCalendarPartRows, MaxCalendarPartRows)
)
}
}
fun setDatePart(datePart: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetDatePart = datePart)