The onboarding screen is the first thing a new user sees; it was a bare
centred title + body + button. Rebuild it as a proper Material 3 Expressive
welcome:
- Branded hero reconstructing the launcher mark (slate squircle + foreground
vector); the denied state adds a lock badge over the corner
- App-name eyebrow, a benefit-led headline, and three trust rows (stays on
device / every calendar together / no tracking) with tonal icon chips
- Full-width filled CTA with a trailing arrow, pinned in a Scaffold bottom bar
clear of the navigation bar; scrollable body for short screens
- "Read-only · no internet permission" footnote — accurate: the app declares
only READ_CALENDAR
- Denied/recovery state reuses the same shell with Open-settings (primary) and
Try-again (text) actions
- 8dp spacing scale, edge-to-edge insets handled via Scaffold
Built with the newly installed material-3 skill's token/component guidance.
Resolves the pre-1.0 polish backlog item.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Busy availability value is the default for nearly every event, so a
"Busy" chip on every detail screen was noise. Show the pill only for Free
(the noteworthy case) and move it to the top-right of the title row instead
of the under-title chip strip. That strip now carries only status/access and
hides entirely when there's nothing noteworthy. Drops the now-unused
event_availability_busy string from both locales.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Round out the read-only model so the detail view shows everything
CalendarContract actually stores, ahead of write support.
Data layer:
- New domain types: Reminder, EventStatus, Availability, AccessLevel,
AttendeeRelationship, AttendeeType; EventDetail gains reminders, status,
availability, accessLevel, eventTimezone, selfStatus and Attendee gains
relationship + type (all defaulted so existing callers compile)
- EventDetailProjection reads STATUS / AVAILABILITY / ACCESS_LEVEL /
EVENT_TIMEZONE / SELF_ATTENDEE_STATUS; AttendeeProjection reads
RELATIONSHIP + TYPE; new ReminderProjection queries CalendarContract.Reminders
- Mappers translate each provider integer code, guarding STATUS's null-vs-0
ambiguity (0 == TENTATIVE) so an absent status reads as Confirmed
- Mapper unit tests cover every new column's codes
Detail UI:
- Status / availability / access chips under the title; cancelled also strikes
the title through
- Reminders card with humanised lead times (plurals, DE + EN)
- Foreign-timezone card, shown only for timed events in a non-device zone
- Attendee role badges + the user's own "Your response: …" line
- http(s) URLs in the description are now tappable
URL field cut: CalendarContract exposes no Events.URL column (only the
CUSTOM_APP_URI app deep-link), so URLs are surfaced by linkifying the
description instead. Recorded in ROADMAP/CHANGELOG.
Version bumped to 0.6.0 / 6.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The date-picker half of M2 is dropped entirely; the "Today" half already
shipped in v0.5. V1 is now feature-complete and only a polish/QA pass
remains before v1.0.
Updated the living planning docs (ROADMAP, STATE, REQUIREMENTS) and the
design spec; corrected the v0.5.0 CHANGELOG note that promised M2 would
return in v1.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M3 — calendar filter: the navigation drawer now hosts the calendar list
inline (grouped by account, colour swatch + checkbox per calendar). Hidden
calendars are persisted app-side and filtered centrally in the repository,
so month/week/day re-filter live the moment a checkbox flips. Drawer trimmed
to Today, the calendar filter, and Settings, with leading icons and a clear
title/section type scale; the stubbed jump-to-date entry (M2) was removed.
M4 — settings: full-screen destination with appearance (theme System/Light/
Dark, Material You dynamic colour auto-disabled < API 31, week start Auto/Mon/
Sun), language (per-app locales via AppCompat, persisted to API 29), and an
about section (version, licence, source link). Theme is driven by one
activity-scoped settings source so changes apply app-wide at once. Week start
now drives the month grid and week view; Auto follows the locale.
Also:
- default view switched from month to week
- Settings screen handles system back (was closing the app)
- fix pre-existing NonObservableLocale/LocalContextConfigurationRead lint
errors in EventDetailScreen so CI lint is green again
- versionName/versionCode bumped to 0.5.0 / 5
Tests: repository hidden-filter (incl. live re-emit), SettingsPrefs round-trip
+ week-start resolution, filter grouping. lint + unit tests + assembleDebug green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tapping an event in the week/day timeline opens a full-screen detail
destination (MD3 list→detail, not a bottom sheet) overlaying the calendar
with a slide transition. One card per field (when, calendar, location,
description, attendees, recurrence) with leading icons; location taps open
a maps intent. Loading/Failure/Success throughout.
Recurrence is humanized from the RRULE — e.g. "Every week on Tue and Thu
until 31 Dec 2026" — covering FREQ/INTERVAL/BYDAY/UNTIL/COUNT with
abbreviated, italicised day names and localized list formatting, falling
back to a generic label for rules it can't render.
Also:
- fix: recurring events failed to open (series row stores DURATION, not
DTEND, so the mapper dropped them as EventNotFound). The detail keeps
them and shows the tapped occurrence's own times from Instances.
- feat: month day cell → opens the day view anchored to that date.
- build: add material-icons-extended (R8 strips unused icons in release).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the throwaway debug screen with the first real calendar UI and a
functional Month <-> Week switcher, on Material 3 Expressive.
Month view (S1):
- Material 3 Expressive card-per-day grid; only the current month's weeks
render (neighbouring days left blank)
- per-day event dots with "+N" overflow, today via primaryContainer
- spring-based press feedback from the active motion scheme
- swipe + drawer navigation, Loading/Failure/Success states
Week view (S2):
- vertical time schedule with overlap-resolved lanes (per-day clipping,
midnight spanning, instant events)
- all-day / multi-day events as connected horizontal spans
- single scroll container (gutter + day columns stay aligned), columns
bundled in a rounded container, noon-centred on load
- top section colour-shifts with the app bar on scroll; swipe navigation,
three states
Shared / infra:
- CalendarHost holds the active view; RootScreen renders it post-permission
- ui/common building blocks: CalendarDrawer, CalendarFailure,
ViewSwitcherPill, pastelize, observable locale, M3 Expressive slide
transition (motionScheme fastSpatialSpec)
- unit tests for the week layout (lanes, clipping, all-day spans)
- build: compileSdk 37, material3 pinned to 1.5.0-alpha21 for Expressive
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-render both locale catalog icons (512x512) from the same logo as the
Android adaptive launcher icon, baking in the foreground group transform
(scale 0.5, pivot 114,108, translate 2,8) over the slate background so the
F-Droid render is pixel-faithful to the on-device icon.
Add design/icon/calendula_launcher.svg as the composed full-bleed source
of truth for store/F-Droid renders.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the simple numeral-only foreground from v0.1.0. The new mark
keeps the kalendae reference explicit (a bold "1" inside the
calendar body) and adds a small calendula bloom as a badge in the
bottom-right corner so the app's "calendula" brand reads at first
glance.
- design/icon/calendula_mark.svg: source SVG (232x232 viewport,
monochrome, lawnicons-style strokes 12/8)
- app/src/main/res/drawable/ic_launcher_foreground.xml: regenerated
as a VectorDrawable preserving the source path data. Off-white
(#FAF6F0) strokes on the existing slate background. Reused as the
<monochrome> slot so Android 13+ themed-icon launchers can recolor
it from wallpaper.
- fdroid-metadata/.../{en-US,de-DE}/icon.png: 512x512 PNG composed
from the same source SVG with the slate background baked in, so
F-Droid clients show a fully rendered tile in the app catalog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inspection of the local Hetzner-synced F-Droid repo after v0.1.0
revealed that fdroidserver only partially picked up Calendula's
metadata: summary was sourced from the YAML fallback (en-US only),
description appeared only for the "de" locale (not de-DE), and no
icon was shown anywhere. Root cause: we wrote Google Play conventions
(short_description.txt, full_description.txt, bare locale code "de")
where fdroidserver expects the fastlane format that the sibling
HouseHoldKeaper repo already uses successfully.
Changes:
- de/ -> de-DE/ (BCP-47 with region matches HHK and is more reliably
parsed by fdroidserver)
- short_description.txt -> summary.txt
- full_description.txt -> description.txt
- Add icon.png (512x512) per locale, composed from the adaptive icon's
foreground path + slate background (rendered via rsvg-convert).
Required because XML-only adaptive icons in the APK aren't
auto-rasterized by fdroidserver.
Verified locally against the previously-broken index by composing the
new icon and renaming the files in-tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan 01 (Foundation & CI) is complete. The app builds, tests pass,
lint is clean, both Gitea workflows are wired. CHANGELOG transitions
the foundation entries from [Unreleased] to [0.1.0] dated 2026-06-08.
STATE.md ticks off Plan 01 execution and points to Plan 02 (Data
Layer + Permission Flow) as the next milestone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>