Add a "Jump to date" row to the drawer (under the View switcher) that opens an M3 date picker and navigates the active view to the chosen day, sliding in from the correct side. Wired across Month/Week/Day, each seeding the picker with its visible anchor (day / week-start / 1st-of-month). Extract the form's private date-picker into a shared ui/common/CalendarDatePickerDialog so the event form and the drawer share one picker; add goToDate() to the Month and Week view models. Reprioritises the roadmap: jump-to-date is now next; duplicate-event drops to the bottom as low-importance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
18 KiB
Calendula — Roadmap
v0.x — Pre-Release
| Version | Milestone | Status |
|---|---|---|
| v0.1 | Foundation & CI | complete |
| v0.2 | Data Layer & Permission Flow | complete |
| v0.3 | Month + Week + Day views, view switcher | complete |
| v0.4 | Event Detail (S4) + humanized recurrence | complete |
| v0.5 | Calendar filter (M3) + Settings (M4) | complete |
| v0.6 | Full event read — surface every readable field | complete |
| v1.0 | First public release — polish pass, F-Droid | complete |
Delivery ran ahead of the original table: Day view (S3) shipped in v0.3 and Event Detail (S4) in v0.4, so the Filter/Settings milestone became v0.5.
Jump-to-date (the date-picker half of M2) was cut from scope and will not ship. The "Today" half of M2 already shipped in v0.5 (drawer entry).
v0.6 — Full event read
Round out the read-only model so a detail view shows everything the system
actually stores, before write support starts. Scope = CalendarContract
columns we don't yet read/display:
- Reminders (
VALARM) — readCalendarContract.Reminders, list lead times - Status — Confirmed / Tentative / Cancelled (cancelled shown struck-through)
- Availability (
TRANSP) — Free / Busy chip - Attendee extras — role (required / optional / organizer) + the user's own
SELF_ATTENDEE_STATUS - Timezone (
EVENT_TIMEZONE) — shown only when it differs from the device zone - URL —
tappable link cardcut:CalendarContractexposes noEvents.URLcolumn (onlyCUSTOM_APP_URI, an originating-app deep-link). URLs are instead surfaced by linkifying the description text - Access level / class (private / confidential) — small chip (optional, trivial)
All of the above shipped in v0.6.0 (2026-06-11).
Deliberately out of v0.6:
- Recurrence exception / modified-occurrence badges —
Instancesalready resolves correct per-occurrence times for display; this only matters for editing, so it folds into v2 CATEGORIES,ATTACH— not reliably exposed byCalendarContract(provider limitation, not our choice)
v1.0 — First Public Release — shipped 2026-06-11
All V1 features shipped, polished, on F-Droid. Read-only calendar. Cut directly after v0.6 (full event read) plus the onboarding-screen polish pass.
Polish backlog (pre-1.0)
Redesign the initial grant-access (permission) screen— done (Material 3 Expressive onboarding, shipped in v0.6.0 / v1.0.0)
v2.0 — Write Support (complete, shipped 2026-06-11)
Delivered in four releasable slices (plan:
docs/superpowers/plans/2026-06-11-03-write-support.md). The V1 spec is a
guide here, not a contract — scope per slice is decided as we go.
| Version | Milestone | Status |
|---|---|---|
| v1.1 | Write foundation — WRITE_CALENDAR, read-only-calendar detection, delete (series + single occurrence) |
complete (shipped 2026-06-11) |
| v1.2 | Create event — form, FAB, last-used-calendar preselect | complete (shipped 2026-06-11) |
| v1.2.1 | Form polish after on-device review — card design system, optional fields + settings defaults, OptionCard dialogs, expressive motion | complete (shipped 2026-06-11) |
| v1.3 | Edit event — shared form, scoped recurring writes (this / following / all), recurrence picker | complete (shipped 2026-06-11) |
| v1.4 | Reminder notifications — see below | complete (shipped 2026-06-11) |
| v2.0 | Conflict dialog, polish pass (store copy refresh, F-Droid screenshots), release | complete (shipped 2026-06-11) |
v2.0 scope was re-cut on 2026-06-11, after v1.4:
- Occurrence edit already shipped early, in v1.3.
- Quick-add is cut from scope: the full form already opens prefilled (visible day, last-used calendar, optional fields hidden), so the sheet would only save one screen transition while adding a second create-surface to maintain. Revisit only if real-world feedback says creation feels heavy.
- Calendar switching while editing moves to the v3 backlog (sync-adapter
minefield:
CALENDAR_IDis sync-adapter-owned, AOSP locks the field; an honest implementation is copy+delete like Google Calendar, with sync-identity and attendee side effects). - Conflict dialog stays (plan 03, decision 5): on save, compare against the row as it was when the form loaded; on external change, ask overwrite / discard. Closes the silent-clobber gap on synced calendars.
v1.4 — Reminder Notifications
Essential, not nice-to-have: Calendula targets users for whom it is their
only calendar app, so reminder delivery can't be delegated to Google/OEM
Calendar. The calendar provider schedules reminders and broadcasts
android.intent.action.EVENT_REMINDER, but it does not post the visible
notification — a calendar app must. We become that app (the Etar model).
Scope:
- Manifest-registered
BroadcastReceiverforEVENT_REMINDER(data schemecontent://com.android.calendar) — wakes us at reminder time, no foreground service. - Read
CalendarContract.CalendarAlerts/Reminders, filter toMETHOD_ALERT/METHOD_DEFAULT(skipMETHOD_EMAIL); post on a dedicated notification channel; tap opens event detail. POST_NOTIFICATIONSruntime permission (API 33+) — requested in onboarding.- Onboarding step: (a) request
POST_NOTIFICATIONS, (b) in-app reminders toggle, default ON, with copy warning that a second calendar app with notifications on will cause duplicate reminders. Mirrored into Settings (reversible).
Deliberately deferred (add only if needed):
- Snooze / dismiss notification actions (Etar has them)
- Battery-optimization exemption prompt for delivery reliability
v2.1 — Month event grid + drawer view tabs (shipped 2026-06-15)
- Month grid shows real events as continuous multi-day bars (not just dots)
- View section in the navigation drawer to switch Month / Week / Day
- Fix: text cursor no longer jumps in event text fields
v2.2 — Tap-to-create + local calendar management (shipped 2026-06-16)
- Tap an empty slot in day/week → create form prefilled with that day + the tapped hour (snapped to the hour, 1 h long)
- Local (device-only) calendar management in a full-screen editor from
Settings → Calendars: create / rename / recolor / delete, with name,
pastel-previewed colour, and description (stored in
CAL_SYNC1) - Synced calendars listed read-only, grouped by account, each with a per-account "manage in source app" deep-link (resolved from the account's authenticator — DAVx5/ICSx5/…) + an add-account shortcut
- Shared
InlineTextFieldextracted toui.common(event form + calendar editor share one input style)
v2.3 — Material 3 grouped-list redesign (shipped 2026-06-16)
A structural + visual pass adopting one shared blueprint (modelled on the ReFra gallery app) across Settings, the calendar manager and the navigation drawer.
- Shared
ui/common/GroupedList.kt:CollapsingScaffold(aLargeTopAppBarwhose title collapses on scroll) +GroupedRow(Position-based corner grouping, press-animated corners,selected+minHeightknobs). - Settings: category hub with About card on top and sliding sub-pages
(Appearance / New event form / Notifications); theme/week-start/language
pickers moved from
DropdownMenuto OptionCard dialogs; token-based icon chips;ic_gitea.xmlfor the About "Source" button. - Calendar manager + drawer restyled to match; shared
CalendarColorChip; drawer scrolls as one with the active view highlighted. - Cards use
surfaceContainerHighfor readable contrast. - Donate button on the About card deferred (target TBD).
Backlog (theme-based, post-v2.1)
The old v3.0 / "daily-driver polish" / "Locations & People" lists are consolidated here by theme. Within a group, (in progress) / (next) mark what is being or about to be worked; everything else is an approved-but-unscheduled idea unless tagged (idea) / (go/no-go) / (rejected). Order across groups is not a commitment.
Near-term sequence (ranked, 2026-06-16)
The theme groups below are the full menu; this is the committed order for the next stretch. Ranking favours finishing the current create/edit + calendar arc before opening new fronts, then cheap-relative-to-value items and ones that unblock a later item. Order is a plan, not a contract — revisit after each lands.
Tier 1 — finish the current arc (create/edit + calendars)
- Tap-to-create in day/week (shipped v2.2.0) — prefilled create from an empty slot
- Local calendar management + "manage in source app" deep-links (shipped v2.2.0)
Settings redesign & restructure(shipped v2.3.0 — grew into the full grouped-list blueprint across Settings + calendars + drawer; see "v2.3" above)Per-event color(shipped v2.4.0) — palette calendars writeEVENT_COLOR_KEY(sync-safe); local/opted-in calendars write a rawEVENT_COLOR; off-by-default setting for no-palette synced calendars Tier 1's create/edit + calendars arc is effectively closed. Duplicate event was deprioritised (2026-06-17) as low-importance and dropped to the bottom of the sequence; the next item is now Jump-to-date (formerly Tier 2).
(Tier 2+ numbering below shifts accordingly; ranking unchanged.)
Settings redesign & restructure (shipped v2.3.0)
The original scope below is kept as a record; the implementation expanded from a sub-screen restructure into the shared grouped-list blueprint (see "v2.3" above).
The settings screen has grown into a flat vertical scroll of divider-separated sections (Appearance, Event form, Notifications, Calendars, Language, About) and will keep accreting rows (per-event-color defaults, default reminder, more calendar entries are all queued). It needs structure before it gets unwieldy.
Decided (2026-06-16): sub-screens, not flat-but-carded. The top level becomes a category list; each category opens its own destination. More M3-idiomatic for a settings surface that will keep growing, and it mirrors the existing Calendars row, which already navigates out to its own screen.
Structure — top-level settings list → category destinations:
- Appearance → theme, dynamic colour, week start
- Event form → the 6 default-field toggles + the hint text
- Notifications → reminders toggle (POST_NOTIFICATIONS flow stays)
- Calendars → already its own screen (
CalendarsScreen); just becomes a peer category row, no change to that screen - Language → single control; keep as a top-level row that opens an OptionCard directly (a whole sub-screen for one choice is overkill)
- About → kept inline on the top-level list as a card (read-only info,
not worth a navigation hop). Card layout, top → bottom:
- Identity — app logo + name "Calendula", with "by Jean-Luc Makiola" as a subtitle beneath the name
- Action buttons (small, button-styled, sit in a row):
- Source — Gitea logo, opens the repo (
about_source_url) - License — opens the LICENSE file on Gitea
- Donate (tentative) — sits next to Source; target TBD (decide before building: Liberapay / Ko-fi / Gitea sponsor / etc.)
- Source — Gitea logo, opens the repo (
- Version — small version number at the bottom of the card
Scope:
- Navigation — add the settings sub-screen destinations alongside the
existing settings/calendars routes in
CalendarHost; back pops to the settings list (mind the existingBackHandlerthat guards against falling through to the activity). - Fix the dialog-pattern violation — theme, week-start and language use
DropdownMenu; the project default is the full-width tonal OptionCard modal (radio/dropdown/text-list dialogs are banned, seeoption-card-modal-style-default). Migrate these selectors to OptionCard. - Visual pass — top-level category rows with leading icons; consistent spacing and row affordances aligned with the event-form card design system.
Out of scope (no new settings features here) — this is a structure + style pass on the existing controls; new toggles ride in with their own features.
Tier 2 — navigation & daily-driver completeness 5. Jump-to-date — drawer date picker (un-cut from V1); cheap, fills the nav gap (next) 6. Agenda view — the missing 4th view; serves daily-driver users and becomes the data source for the widget
Tier 3 — platform reach (depends on Tier 2) 7. Home-screen widget — built on the agenda data source from #6 8. App shortcuts (launcher long-press → New event); cheap, optional quick-settings tile
Tier 4 — interop & bigger-ticket 9. Share event as .ics + receive/open .ics into a prefilled create form 10. Default reminder applied to new events; then snooze/dismiss notification actions 11. Drag & drop rescheduling in day/week — big-ticket, own slice (recurring drops reuse the scope dialog)
Gated — explicit go/no-go before any work (mostly INTERNET-permission calls)
- Remote calendar create/edit (re-implements DAVx5; INTERNET + credential storage)
- Locations & People — contact address picker (no-permission, one-shot) is the safe entry; OSM autocomplete needs INTERNET
- Move event to another calendar — sync-adapter minefield (copy+delete model)
Bottom — deprioritised, not important
- Duplicate event (detail action → prefilled create form) — moved here 2026-06-17; cheap but low value, pick up only if asked
Unranked / fill-in — pinch-to-zoom time scale, tablet/foldable layouts, full-text search, ICS file import. Pulled in opportunistically, not sequenced.
Debatable calls worth a second look: widget (#7) vs .ics interop (#9) ordering; whether drag-drop (#11) jumps ahead given its daily-driver impact.
Navigation & views
Tap an empty slot in day/week → create form prefilled with that date+time, snapped to the hourshipped v2.2.0 (long-press variant not added — single tap covers it)- Agenda view (fourth view: upcoming events grouped by day; also the natural data source for a future widget)
- Jump to date — drawer date picker (un-cut from V1)
- Pinch-to-zoom time scale in day/week
- Tablet / foldable layouts (was v3.0)
- Full-text search (was v3.0)
Event editing & creation
- Drag & drop rescheduling in day/week (recurring drops reuse the scope dialog) — big-ticket, own slice
- Duplicate event (detail action → prefilled create form)
- Per-event color (
Events.EVENT_COLOR, OptionCard picker in the form) (next) — chosen to follow the in-progress tap-to-create + calendar management work: reuses the color-picker component and palette plumbing being built for local calendar management, and finishes the create/edit theme.EVENT_COLOR/EVENT_COLOR_KEYfrom the calendar's color list (Colorstable,TYPE_EVENT); falls back to the calendar color when unset.
Calendars & accounts
Create / manage local (device-only) calendarsshipped v2.2.0 — name + color + description; rename / recolor / delete the calendars the app owns. Inserted underACCOUNT_TYPE_LOCALas a sync adapter; description inCAL_SYNC1. Full-screen "Calendars" editor reached from Settings.Per-calendar "manage in source app" deep-linkshipped v2.2.0 — for synced calendars, open the app the calendar actually came from based on itsACCOUNT_TYPE(DAVx5bitfire.at.davdroid, Googlecom.google, …); fall back to system account/sync settings. Plus an "add account" entry into system Accounts. Honest boundary for remote calendars.- Remote calendar create/edit (go/no-go) — creating a CalDAV
collection (
MKCALENDAR) or a Google calendar means an in-app sync client: INTERNET permission, credential storage, the full server round-trip — i.e. re-implementing DAVx5. DAVx5 exposes no public intent to delegate the create to it. Cosmetic local edits (color/name) to an existing synced row are possible but don't propagate to the server and may be overwritten on next sync — not promised. Same explicit go/no-go gate as the OSM/INTERNET item below. - Move event to another calendar (copy+delete model with a consequences
warning — deferred from v2.0;
CALENDAR_IDis sync-adapter-owned) (was v3.0)
Reminders, round two
- Snooze + dismiss actions on the notification (snooze needs an exact-alarm / WorkManager decision)
- Settings default reminder applied to new events
Sharing & interop
- Share event as .ics + open/receive .ics into a prefilled create form (front-runs the import below)
- ICS file import (drag-and-drop) (was v3.0, optional)
Platform & launchers
- Home-screen widget (was v3.0)
- App shortcuts (launcher long-press → New event), maybe a quick-settings tile
Locations & People (go/no-go, captured 2026-06-11)
Beyond classic calendar-client scope; discussed, deliberately not planned in detail yet:
- Contact address picker for the location field via the system picker
(
ACTION_PICKon postal addresses) — one-shot, needs no READ_CONTACTS, fits the privacy story. Same mechanism later for picking emails. - OSM address autocomplete in the location field (type "Brandenburger Tor" → tap suggestion → resolved address inserted). Backend would be Photon (Nominatim's public policy forbids autocomplete). Requires the INTERNET permission — first dent in the "no network access" promise; if built: opt-in (off by default), honest copy, configurable endpoint for self-hosters, onboarding footnote + F-Droid copy reworded. This trade-off is an explicit go/no-go decision before any work starts.
- Inline contact suggestions while typing (needs READ_CONTACTS) — only if the picker proves clunky.
- Attendee editing / invites from contacts — own milestone; writing
Attendeesrows touches sync-adapter invitation behavior (Google vs DAVx5 differ).
Consciously rejected
- Travel time / weather / smart suggestions (network, core-promise conflict)
- Natural-language quick entry (high effort, locale-fragile; the prefilled form already covers fast entry)
- Quick-add sheet (the prefilled full form already covers it — cut in v2.0)