Jean-Luc Makiola f0e2e12939 feat(edit): event editing — shared form, scoped recurring writes, recurrence picker (v1.3)
The create form (v1.2) now edits: a pencil on the detail screen (writable
calendars only, contextual WRITE upgrade like delete) opens it prefilled via
EventDetail.toEditForm; populated sections always show, the calendar is
fixed, and a dirty-check writes only changed columns (pristine saves are
no-ops). Saving a dirty recurring event parks in SaveUiState.AwaitingScope
and asks how far the change reaches (Google model): "only this event" =
modified-occurrence exception via CONTENT_EXCEPTION_URI (empty optionals as
explicit NULLs since the provider clones the parent row), "this and all
following" = series split (insert new event first, then truncate), "all
events" = series-row update with the time delta applied to the series
DTSTART. A changed rule drops the exception option. Delete gained the same
middle scope.

Recurrence: EventForm.rrule + SimpleRecurrence (FREQ/INTERVAL/UNTIL/COUNT +
weekly BYDAY with locale-ordered weekday toggles) behind a picker on create
and edit; unrepresentable rules render humanized (shared ui/common
RecurrenceText) and survive verbatim. UNTIL validation flags rules ending
before the event starts.

Provider lessons baked in (verified on-device via adb probes): instance
caches regenerate only from an update's own values, so truncation sends the
full time-column set (truncateSeries) — RRULE-only updates left a stale
duplicate occurrence on the split day; UNTIL is written as the local end of
day in UTC (toRRule(zone), previousLocalDayEndUtcMillis) so UTC+x zones
can't leak an extra day. Reminder edits reconcile against actual provider
rows, keeping untouched rows' methods.

Tests: RecurrenceTest (parse/render/round-trip, truncation), update/exception
mapper paths, repository pass-throughs, prefill + populatedFields, raw-title
mapper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:57:32 +02:00

Calendula

A modern Material 3 Expressive calendar app for Android.

Calendula is named after the flower of the same name, whose name comes from the Latin kalendae — the first day of the month — the same root as the word "calendar". Calendula reads from Android's built-in CalendarContract, so any calendar source synced to your device (CalDAV via DAVx5, Google, local, WebCal subscriptions, ...) is shown.

Features (V1)

  • Month, Week, and Day views
  • Read-only event details (write support comes in V2)
  • Multi-calendar visibility toggle
  • Material You Dynamic Color (Android 12+)
  • Light/Dark theme follows system
  • German + English UI

Building

Requires Android SDK 36 and JDK 17. The Gradle wrapper is checked in, so no host Gradle install is needed:

# Build debug APK
./gradlew assembleDebug

# Run unit tests
./gradlew test

# Run lint
./gradlew lint

If your default JDK is something other than 17, set JAVA_HOME explicitly:

JAVA_HOME=/path/to/jdk-17 ./gradlew assembleDebug

License

MIT — Jean-Luc Makiola, 2026

Description
A modern Material 3 Expressive calendar app for Android
Readme MIT 3.1 MiB
v2.7.0 Latest
2026-06-18 14:24:35 +00:00
Languages
Kotlin 99.6%
Python 0.4%