All-day items are date-only in iCalendar and are stored as UTC midnight of their calendar date — the convention the tasks and calendar providers read back (OpenTasks resolves them through DateTime.toAllDay(), which discards the time and resolves the remaining date against UTC). formatDate/formatDateTime/formatDateTimeCompact resolved every instant against ZoneId.systemDefault(), so an all-day value rendered as the *previous* day for anyone west of UTC: a task stored as 2026-07-20T00:00Z showed as 19 Jul in New York. East of UTC it happened to land on the right day, which is why this went unnoticed. formatDate now takes an allDay flag (defaulting to false, so existing call sites are unaffected) and all-day values resolve against UTC. Callers must pass allDay at the call site — agendula and calendula both have sites that need updating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
floret-kit
The shared Material 3 Expressive design system + plumbing for the Floret family of Android apps (Calendula, Agendula, and future siblings).
"Floret" is the family's design language: a Calendula flower head is a cluster of many small florets, and each app is one of them. This repo is the bloom they share — so a new app doesn't reinvent a settings screen, an edit form, or a theme; it draws from here.
How it's consumed
Each app embeds this repo as a git submodule and wires it in with a Gradle composite build, so the kit is always built from source (no published artifacts — keeps every app reproducible for F-Droid):
// app's settings.gradle.kts
includeBuild("floret-kit")
// app module's build.gradle.kts
implementation("de.jeanlucmakiola.floret:core-time")
Gradle's dependency substitution maps de.jeanlucmakiola.floret:<module> to the
local source module — edit the kit and the app picks it up immediately. Android
modules need an SDK location for the included build: set ANDROID_HOME (CI) or
add a gitignored <app>/floret-kit/local.properties with sdk.dir (locally).
See docs/ARCHITECTURE.md for the full consumption model.
Modules
| Module | Type | What it holds |
|---|---|---|
core-time |
JVM | Date/time helpers: DayWindow local-day windows, locale/zone-aware Instant formatting, TimeBridge (millis ↔ Instant). No Android, no deps. |
core-reminders |
JVM | Reminder-lead plumbing: the lead-time unit model (ReminderUnit, decomposeReminderMinutes), the per-target override model (ReminderOverride + reminderLeadFor / applyReminderOverride) and the stored-format codec (ReminderOverrideCodec, separators configurable per app). Each app layers its own DataStore + string labels on top. |
core-locale |
Android | Everything "what language are we in". Per-app language plumbing (AppLanguage): read the shipped languages from the app's res/xml/locales_config.xml, get/set the applied language via AppCompatDelegate, render each language's autonym. Plus currentLocale() (observes the applied language from Compose) and localizedDateFormatter(locale, skeleton) (lays a field skeleton out in the locale's own order). The app passes its own locales_config. |
core-crash |
Android | Privacy-respecting on-device crash capture + report dialog + issue-tracker hand-off. Parameterized per app via CrashConfig. |
identity |
Android | The M3 Expressive theme factory FloretExpressiveTheme(…), rememberNavSlideSpec(), the content transitions (expandEnter/collapseExit/itemEnter/…) and the predictiveBack peek. Each app supplies its own seed/palette. |
components |
Android | Shared Compose vocabulary + recipes: GroupedSurface/GroupedRow, InlineTextField, OptionCard, CollapsingScaffold, OptionPicker, ReorderableColumn, DebugRibbon, pastelize(), DialogControls, OnboardingScaffold, OptionalFormSection, AboutCard, LanguagePickerRow. |
The principle: the mechanics are shared, the look stays per-app. See
docs/ROADMAP.md for what's extracted, deferred, and
deliberately not shared.
Docs
See docs/ — start with docs/README.md
(ARCHITECTURE, ROADMAP) and
CONTRIBUTING.md to add or change a module.
CHANGELOG.md tracks what landed.
Build
./gradlew :core-time:test # JVM module tests
./gradlew :core-crash:testDebugUnitTest
./gradlew build # everything (Android modules need an SDK; see above)