Jean-Luc Makiolaandmakiolaj 96509d0f9d glance: widget refresh helpers (updatePlaced, refresh stamp, WidgetRefresher, throttled re-arm) (#18)
Widget-refresh plumbing that Calendula's 2.24.0 widget refactor needs, and that Agendula already hand-rolls.

- `GlanceAppWidget.updatePlaced(context, receiverClass)` redraws every placed instance using ids from `AppWidgetManager` + `getGlanceIdBy`. `updateAll` uses Glance's provider-name map, which is empty in a process that hasn't hosted a widget session yet, so the redraw gets dropped (calendula#18, calendula#420).
- `updatePlaced(..., bumpRefreshStamp = true)` also moves each instance's `currentRefreshStamp()`. An update to a running session only recomposes and never re-runs `provideGlance`, so a widget that keys its data load on the stamp reloads there too. This is the per-widget `data_stamp` idea from calendula#420.
- `WidgetRefresher` turns a burst of "data changed" requests into one debounced redraw, the generic part of Agendula's `TaskWidgetUpdater`. Calendula will use it for its own writes, so they don't have to wait for `PROVIDER_CHANGED`.
- `FloretWidgetUpdateReceiver.extraRearmInterval` lets extra actions also re-arm the rollover alarm, at most once per interval. This moves Calendula's 15-minute `PROVIDER_CHANGED` backstop upstream. It defaults to off, so Agendula is unchanged.

Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/floret-kit/pulls/18
2026-10-21 17:56:00 +02:00

floret-kit

The shared Material 3 Expressive design system + plumbing for the Floret family of Android apps (Calendula, Agendula, Clockula, 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) and relativeTimeAgo(…) ("5 minutes ago"). The app passes its own locales_config.
core-prefs Android The typed DataStore layer: Pref<T> + PrefStore (typed read/write, distinct-until-changed flows), toEnum() for values that degrade instead of throwing, and the family's shared appearance prefs (ThemeMode, dynamicColor). The store's name and construction stay with the app.
core-di JVM The shared DI vocabulary: @IoDispatcher, @DefaultDispatcher, @ApplicationScope and the Hilt module that provides them.
core-android Android Small Android plumbing: broadcastTicks (a Flow that ticks on system broadcasts), a coroutine BroadcastReceiver.goAsync { }, Context.hasPermission, AlarmManager.setWakeup (exact, or inexact allow-while-idle without the capability) and Flow.stateWhileSubscribed. No Compose, no DI.
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/GroupedSwitchRow/GroupedHint, IconInfoCard, InlineTextField, OptionCard, CollapsingScaffold, OptionPicker/OptionRows/CustomExpandRow, ReorderableColumn, DebugRibbon, pastelize(), DialogControls, OnboardingScaffold, OptionalFormSection, AboutCard, LanguagePickerRow, SnackChip.
glance Android Home-screen-widget plumbing on Jetpack Glance: FloretGlanceTheme, the rounded WidgetSurface (solid/tinted/transparent backgrounds), WidgetHeader/WidgetActionButton, the position-aware WidgetGroupedRow, WidgetMessage, WidgetRollover (the midnight-rollover alarm), FloretWidgetUpdateReceiver, and the refresh helpers updatePlaced / WidgetData / WidgetRefresher. App supplies its own colour schemes, data and receiver glue.

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 :core-prefs:testDebugUnitTest
./gradlew :core-di:test
./gradlew :core-android:testDebugUnitTest
./gradlew build                    # everything (Android modules need an SDK; see above)
S
Description
Build infrastructure only — canonical repo: https://codeberg.org/jlmakiola/floret-kit
Readme MIT
500 KiB
Languages
Kotlin 100%