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

@@ -59,12 +59,21 @@ changes on top of that:
* **A new part shows today's agenda.** `CalendarPartProvider`
(`app/ui/.../widgets/clock/parts/`) is toggled with the *Events* switch
(`clockWidgetCalendarPart`). It lists the events that are still running or upcoming today, plus
today's all-day events, capped at 4 rows — if there are more, the last row is a "+N more events"
hint that opens the calendar app. Tapping a row opens the event
today's all-day events, capped at `clockWidgetCalendarPartMaxRows` rows (1..8, default 4) — if
there are more, the rows are followed by a "+N more events" hint that opens the calendar app.
Tapping a row opens the event
(`CalendarEvent.launch`, i.e. `ACTION_VIEW` on `content://com.android.calendar/events/<id>`,
which KashCal handles). Rows reuse `CalendarEvent.getSummary()` from
`ui/launcher/search/calendar/CalendarItem.kt` (made `internal` for this), so an all-day event
reads "All day" and a timed one reads e.g. "09:00 09:30".
* **The agenda picks its calendars.** `clockWidgetCalendarPartExcludedCalendars` (empty = all)
hides individual calendars; the query's exclusion set is that set plus the search settings'
exclusions, because the picker only ever offers calendars the search settings leave enabled
(`ClockWidgetSettingsScreenVM.calendars` filters them out of `CalendarRepository.getCalendars()`,
and a "Manage calendars" button opens `Settings → Search → Calendar` via the
`ROUTE_SEARCH_CALENDARS` route in `SettingsActivity`). The whole calendar configuration lives in
`ConfigureCalendarPart.kt`, shown by `ConfigureClockWidgetSheet` while the Events part is on —
the agenda deliberately ignores the calendar *widget*'s own `excludedCalendarIds`.
Rankings, in the order the zone prefers them: `CalendarPartProvider` 70 when an event is running or
starts within 30 minutes, else 30 while there are events left today, else 0 (so the date part keeps
@@ -78,8 +87,10 @@ launcher only re-query when they become visible). `PartProvider.setTime()` is ca
by the clock widget; the provider folds that into its ranking instead of re-querying.
The clock settings themselves are in `core/preferences`: `LauncherSettingsData` (new fields
`clockWidgetCalendarPart`, `clockWidgetDynamicZoneSlots`), `ClockWidgetParts` and the
`ClockWidgetSettings` accessors in `core/preferences/.../ui/ClockWidgetSettings.kt`, and the UI
`clockWidgetCalendarPart`, `clockWidgetCalendarPartExcludedCalendars`,
`clockWidgetCalendarPartMaxRows`, `clockWidgetDynamicZoneSlots`), `ClockWidgetParts` and the
`ClockWidgetSettings` accessors in `core/preferences/.../ui/ClockWidgetSettings.kt` (including the
`MinCalendarPartRows`/`MaxCalendarPartRows` bounds), and the UI
state in `ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt`. New strings are English-only in
`core/i18n/src/main/res/values/strings.xml`; translations come from Crowdin upstream.