Both functions lived in Calendula's ui/common/LocaleSupport.kt. Neither is
calendar-specific — "lay this date out the way this language writes dates" is
family plumbing — so they move here, where Agendula can reach them too.
localizedDateFormatter(locale, skeleton) takes a field list ("LLLLy", "dMMMy")
and lets the locale arrange it, which is the only way to render a date correctly
in a language you didn't hand-write a template for. Dropping a field from the
skeleton drops it from the output, so callers ask for "no year" by omitting it
rather than post-processing a longer pattern.
currentLocale() reads the display locale from LocalConfiguration. This module's
own AppLanguage.apply() changes the language inside a running process, so a
locale captured once goes stale; keying on the configuration makes the UI follow
the switch.
core-locale gains Compose (runtime + ui only) for currentLocale(). Its build file
previously declared itself Compose-free, but the alternative — parking the hook
in a Compose module that depends back on this one — splits one concern across two
modules to honour a comment. core-crash is the precedent for a core-* module
carrying Compose.
Leaves core-time's Instant.formatDate() family alone despite the overlap: it is
unused by Calendula but visible to Agendula, so reconciling it is that app's
call, not a drive-by here. Noted in the changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.7 KiB
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)