Files
calendula/.planning/ROADMAP.md
Jean-Luc Makiola 2943f3945d
All checks were successful
CI / ci (push) Successful in 6m17s
feat(nav): jump-to-date action in the navigation drawer
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>
2026-06-17 09:24:49 +02:00

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) — read CalendarContract.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
  • URLtappable link card cut: CalendarContract exposes no Events.URL column (only CUSTOM_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 — Instances already resolves correct per-occurrence times for display; this only matters for editing, so it folds into v2
  • CATEGORIES, ATTACH — not reliably exposed by CalendarContract (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) screendone (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_ID is 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 BroadcastReceiver for EVENT_REMINDER (data scheme content://com.android.calendar) — wakes us at reminder time, no foreground service.
  • Read CalendarContract.CalendarAlerts / Reminders, filter to METHOD_ALERT / METHOD_DEFAULT (skip METHOD_EMAIL); post on a dedicated notification channel; tap opens event detail.
  • POST_NOTIFICATIONS runtime 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 InlineTextField extracted to ui.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 (a LargeTopAppBar whose title collapses on scroll) + GroupedRow (Position-based corner grouping, press-animated corners, selected + minHeight knobs).
  • Settings: category hub with About card on top and sliding sub-pages (Appearance / New event form / Notifications); theme/week-start/language pickers moved from DropdownMenu to OptionCard dialogs; token-based icon chips; ic_gitea.xml for the About "Source" button.
  • Calendar manager + drawer restyled to match; shared CalendarColorChip; drawer scrolls as one with the active view highlighted.
  • Cards use surfaceContainerHigh for 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)

  1. Tap-to-create in day/week (shipped v2.2.0) — prefilled create from an empty slot
  2. Local calendar management + "manage in source app" deep-links (shipped v2.2.0)
  3. Settings redesign & restructure (shipped v2.3.0 — grew into the full grouped-list blueprint across Settings + calendars + drawer; see "v2.3" above)
  4. Per-event color (shipped v2.4.0) — palette calendars write EVENT_COLOR_KEY (sync-safe); local/opted-in calendars write a raw EVENT_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.)
    • 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 existing BackHandler that 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, see option-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 hour shipped 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_KEY from the calendar's color list (Colors table, TYPE_EVENT); falls back to the calendar color when unset.

Calendars & accounts

  • Create / manage local (device-only) calendars shipped v2.2.0 — name + color + description; rename / recolor / delete the calendars the app owns. Inserted under ACCOUNT_TYPE_LOCAL as a sync adapter; description in CAL_SYNC1. Full-screen "Calendars" editor reached from Settings.
  • Per-calendar "manage in source app" deep-link shipped v2.2.0 — for synced calendars, open the app the calendar actually came from based on its ACCOUNT_TYPE (DAVx5 bitfire.at.davdroid, Google com.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_ID is 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_PICK on 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 Attendees rows 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)