diff --git a/CHANGELOG.md b/CHANGELOG.md index d3fe2fb..fa82398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,9 @@ are no binary releases; "version" tracks the shared `version` in the root build forwarded to its `OptionPicker`, so an app with a "help translate" row above the languages can drop its own copy of the row. Both default to today's behaviour. +- **`core-locale`** — `relativeTimeAgo(epochMillis, nowMillis, minResolution)`, + the platform-localised "5 minutes ago" Calendula's backup and special-dates + status lines build through `DateUtils.getRelativeTimeSpanString`. - **`identity`** — a pure-black (AMOLED) dark theme. `ColorScheme.toPureBlack()` sets `background`, `surface`, `surfaceDim` and `surfaceContainerLowest` to diff --git a/README.md b/README.md index 821e7ef..ba1f958 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the full consumption mode |--------|------|---------------| | `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-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` + `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-crash` | Android | Privacy-respecting on-device crash capture + report dialog + issue-tracker hand-off. Parameterized per app via `CrashConfig`. | diff --git a/core-locale/src/main/kotlin/de/jeanlucmakiola/floret/locale/RelativeTime.kt b/core-locale/src/main/kotlin/de/jeanlucmakiola/floret/locale/RelativeTime.kt new file mode 100644 index 0000000..376a724 --- /dev/null +++ b/core-locale/src/main/kotlin/de/jeanlucmakiola/floret/locale/RelativeTime.kt @@ -0,0 +1,17 @@ +package de.jeanlucmakiola.floret.locale + +import android.text.format.DateUtils + +/** + * "5 minutes ago" / "in 2 hours" for [epochMillis] relative to [nowMillis], in the + * process locale, via the platform's `DateUtils`. Spans shorter than + * [minResolution] read as the smallest unit ("0 minutes ago"), so a status line + * doesn't tick by the second. + * + * Needs a real Android runtime, like [localizedDateFormatter]. + */ +fun relativeTimeAgo( + epochMillis: Long, + nowMillis: Long = System.currentTimeMillis(), + minResolution: Long = DateUtils.MINUTE_IN_MILLIS, +): String = DateUtils.getRelativeTimeSpanString(epochMillis, nowMillis, minResolution).toString()