Rename app Floret → Agendula
All checks were successful
CI / ci (push) Successful in 8m33s

Floret is promoted to the family / design-language (shared-kit) name; the
tasks app itself becomes Agendula (de.jeanlucmakiola.agendula) — agenda
('things to be done') + Calendula's -ula, a twin of the Calendula name.

Renames the package, namespace, applicationId, rootProject.name, app_name,
FloretApp/FloretNavHost/FloretTransitions classes, theme, F-Droid metadata
dir, CI artifact name, and docs. The botanical word 'florets' is preserved in
the name-origin prose, which is rewritten to Agendula's etymology. Clean
build + unit tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 23:10:24 +02:00
parent d84ac60757
commit 76a0139fda
93 changed files with 408 additions and 403 deletions

View File

@@ -1,6 +1,6 @@
# Floret — architecture
# Agendula — architecture
This document describes how Floret is built **as it stands today**. For the
This document describes how Agendula is built **as it stands today**. For the
*why* behind the big decisions and the long-term plan, see
[`PLAN.md`](PLAN.md); for status and what's next, see [`ROADMAP.md`](ROADMAP.md).
@@ -8,10 +8,10 @@ This document describes how Floret is built **as it stands today**. For the
## 1. The thesis in one sentence
Floret is a Material 3 Expressive **front-end** over the OpenTasks
Agendula is a Material 3 Expressive **front-end** over the OpenTasks
`TaskContract` provider — it reads, writes, and reminds on top of a tasks store
that some other app (DAVx5, SmoothSync, DecSync CC, tasks.org, …) syncs over
CalDAV. **Floret owns no database and no sync stack.** It is the task-list
CalDAV. **Agendula owns no database and no sync stack.** It is the task-list
sibling to [Calendula](https://gitea.jeanlucmakiola.de/makiolaj/calendula),
which does the same thing for `CalendarContract`.
@@ -69,7 +69,7 @@ Both are bound in Hilt in `data/di/DataModule.kt`.
## 3. Module & package layout
Single `:app` module (Posture A). Package root `de.jeanlucmakiola.floret`.
Single `:app` module (Posture A). Package root `de.jeanlucmakiola.agendula`.
| Package | Contents |
|---|---|
@@ -80,7 +80,7 @@ Single `:app` module (Posture A). Package root `de.jeanlucmakiola.floret`.
| `data/di/` | `DataModule` (binds + provides), `Qualifiers` (`@IoDispatcher`). |
| `data/demo/` | `DemoSeeder` (debug-only sample data). |
| `ui/` | `theme/`, `common/` (GroupedList, ListChip), `lists/`, `tasklist/`, `detail/`, `edit/`, `settings/`, `permission/` (each a ViewModel + UiState; `lists` also has its screen), `RootScreen`. |
| root | `FloretApp` (Hilt app), `MainActivity`. |
| root | `AgendulaApp` (Hilt app), `MainActivity`. |
---
@@ -106,7 +106,7 @@ runtime perms for the active provider.
### 4.2 `TasksContract`
A vendored subset of the Apache-2.0 OpenTasks `TaskContract` — column names,
table paths, status/priority constants, the local-account type. Floret does
table paths, status/priority constants, the local-account type. Agendula does
**not** take a runtime dependency on OpenTasks; the authority is injected from
`ProviderResolver`, never hardcoded in the contract.
@@ -117,7 +117,7 @@ occurrence is a row with the joined list colour, account, etc.), maps each
cursor row through `ColumnReader``TaskMapper` → domain `Task`, and exposes
the result as a `Flow`. A `ContentObserver` on the active authority's
Tasks/TaskLists URIs bridges into the Flow via `callbackFlow`, so **any change
re-emits** — Floret's own writes *and* external sync (DAVx5 pulling new tasks)
re-emits** — Agendula's own writes *and* external sync (DAVx5 pulling new tasks)
update the UI live, and multiple sync sources coexist in one list.
### 4.4 Writes — repository API
@@ -170,7 +170,7 @@ priority / etc.). None of this touches Android, which is why it's all in
## 6. Reminders — the one subsystem that does NOT mirror Calendula
Calendula relies on the calendar provider broadcasting `EVENT_REMINDER`. **Tasks
providers broadcast nothing**, so Floret schedules its own (`data/reminders/`):
providers broadcast nothing**, so Agendula schedules its own (`data/reminders/`):
- **`ReminderScheduler.sync()`** reads upcoming, non-closed, due-dated tasks
within a rolling **30-day window**, computes each trigger as `due lead`
@@ -186,7 +186,7 @@ providers broadcast nothing**, so Floret schedules its own (`data/reminders/`):
sync changed the data). The store lets each run diff like Calendula diffs
reminder rows.
This is the single largest piece of genuinely-new code in Floret.
This is the single largest piece of genuinely-new code in Agendula.
---
@@ -201,7 +201,7 @@ This is the single largest piece of genuinely-new code in Floret.
domain, and `TasksRepository` do not change** — only the resolver's default and
some manifest perms.
Bundling the provider bundles **storage, not sync**Floret stays a pure
Bundling the provider bundles **storage, not sync**Agendula stays a pure
front-end over open backends either way.
---
@@ -228,8 +228,8 @@ checkbox/FAB/swipe motion).
Hilt, `SingletonComponent`. `DataModule` has a `@Binds` module
(`TasksDataSource``AndroidTasksDataSource`, `TasksRepository`
`TasksRepositoryImpl`) and a `@Provides` module (the `floret_prefs` DataStore,
the `@IoDispatcher`). `FloretApp` is the `@HiltAndroidApp` entry point;
`TasksRepositoryImpl`) and a `@Provides` module (the `agendula_prefs` DataStore,
the `@IoDispatcher`). `AgendulaApp` is the `@HiltAndroidApp` entry point;
`MainActivity` is `@AndroidEntryPoint`. ViewModels get the repository injected.
---

View File

@@ -1,23 +1,25 @@
# Floret — implementation plan
# Agendula — implementation plan
> A modern Material 3 Expressive **task** app for Android. Reads, writes, and
> reminds — on top of an existing tasks provider (synced by DAVx5 / SmoothSync /
> DecSync over CalDAV), with no own sync stack.
>
> Sibling to **Calendula**. Calendula is a calendar over `CalendarContract`;
> Floret is a to-do list over the **OpenTasks `TaskContract` provider**. A
> Calendula flower head is made of many small *florets* — the individual items
> that make up the bloom.
> Agendula is a to-do list over the **OpenTasks `TaskContract` provider**. The
> name mirrors Calendula's: *agenda* (Latin, “things to be done”) + the `-ula`
> ending — and a Calendula flower head is itself a cluster of small *florets*,
> so the two apps are florets of one bloom.
Working identifiers (placeholder, easily renamed):
`applicationId = de.jeanlucmakiola.floret`, app name **Floret**.
Identifiers: `applicationId = de.jeanlucmakiola.agendula`, app name **Agendula**
(renamed from the working title *Floret*, which was promoted to the shared
family / design-language name).
---
## 0. The thesis this app embodies
A nice M3-Expressive front end over open backends, no reinvented storage or
sync. Calendula proved the pattern against the OS calendar provider. Floret
sync. Calendula proved the pattern against the OS calendar provider. Agendula
applies it to tasks. The crucial difference: **there is no OS tasks provider**,
so we depend on a tasks *provider app* being present — exactly as Calendula
depends on a sync app like DAVx5 for CalDAV.
@@ -46,7 +48,7 @@ string leak above the data layer.**
Calendula's layering is the template. Lift these **verbatim or near-verbatim**:
| Area | From Calendula | Change for Floret |
| Area | From Calendula | Change for Agendula |
|---|---|---|
| Gradle setup | `build.gradle.kts`, `settings.gradle.kts`, `gradle/libs.versions.toml`, wrapper, `key.properties` flow, versionCode-from-tag CI | namespace/appId only |
| Build config | AGP 9.2.1, Kotlin 2.3.21, KSP, Hilt 2.59.2, compileSdk 37 / minSdk 29 / targetSdk 36, Java 17 | identical |
@@ -65,7 +67,7 @@ Calendula's layering is the template. Lift these **verbatim or near-verbatim**:
`TaskContract` data layer, the task screens, and a **self-scheduled reminder
engine** (see §6 — the one place Calendula's pattern does *not* carry over).
### What does NOT exist here (why Floret is simpler than Calendula)
### What does NOT exist here (why Agendula is simpler than Calendula)
No month/week/day grid rendering. No recurrence-scoped writes ("this & following"
vs "whole series"). No timezone/all-day gymnastics. Tasks are a flat-or-lightly-
@@ -76,7 +78,7 @@ nested list with a due date and a checkbox.
## 2. Module & package layout
Single `:app` module for A (mirrors Calendula). Package root
`de.jeanlucmakiola.floret`.
`de.jeanlucmakiola.agendula`.
```
domain/
@@ -111,10 +113,10 @@ ui/
edit/ TaskEditScreen + VM + UiState
settings/ (copy + adapt)
permission/ provider-presence + permission + POST_NOTIFICATIONS onboarding
FloretHost.kt nav host (mirrors CalendarHost)
AgendulaHost.kt nav host (mirrors CalendarHost)
RootScreen.kt
widget/ Glance task widget (later milestone)
FloretApp.kt, MainActivity.kt
AgendulaApp.kt, MainActivity.kt
```
---
@@ -217,9 +219,9 @@ Material 3 Expressive throughout: `MaterialExpressiveTheme`,
Three gates, in order:
1. **Provider present?** `ProviderResolver.detect()`. If none → a screen
explaining Floret needs a tasks provider, with one-tap links to install
explaining Agendula needs a tasks provider, with one-tap links to install
**OpenTasks** (FOSS) or **tasks.org**, plus "I use DAVx5 — set its Tasks app".
(This is Floret's "needs DAVx5" moment. Disappears entirely under Posture B.)
(This is Agendula's "needs DAVx5" moment. Disappears entirely under Posture B.)
2. **Tasks read/write permission** — request the active provider's runtime perms
(`org.dmfs.permission.*` etc.). Declared in the manifest *and* requested at
runtime; resolved dynamically from the detected provider.
@@ -235,7 +237,7 @@ exactly like Calendula's launcher query.
Calendula relies on the **calendar provider broadcasting `EVENT_REMINDER`** and
just posts the notification (Etar model). **Tasks providers do not broadcast
reminders.** So Floret must schedule its own:
reminders.** So Agendula must schedule its own:
- `DueReminderScheduler` (AlarmManager, exact alarms via `USE_EXACT_ALARM` /
`SCHEDULE_EXACT_ALARM`) sets an alarm per task at `DUE` minus the chosen
@@ -273,7 +275,7 @@ receiver. **No `EVENT_REMINDER` receiver** (doesn't apply).
## 8. Milestones
- **M0 — Skeleton.** Copy Gradle/version-catalog/theme/DI/app+activity from
Calendula, rename to Floret. Builds, shows themed empty scaffold. *(~½ day)*
Calendula, rename to Agendula. Builds, shows themed empty scaffold. *(~½ day)*
- **M1 — Read path.** `TasksContract` + `ProviderResolver` + `OpenTasksDataSource`
+ repository. Lists overview + task list render real synced tasks (read-only),
live-updating via ContentObserver. *(the meaty milestone)*
@@ -297,21 +299,22 @@ M4 (subtasks) and M5 (reminders) are the two spots needing real thought.
## 9. Open decisions / to verify
1. **Name**`Floret` is the working title (florets compose a Calendula head).
Confirm or replace before M0 (touches appId, namespace, package).
1. ~~**Name**~~**Agendula** (final): *agenda* + Calendula's `-ula`. Renamed
from the working title *Floret*, which was promoted to the family /
design-language name.
2. **tasks.org provider authority** — verify on a real device; OpenTasks
(`org.dmfs.tasks`) is the certain target for v1.
3. **jtx Board** — support its richer contract later, or stay OpenTasks-only?
4. **B authority choice** — bundling `org.dmfs.tasks` makes Floret a *replacement*
4. **B authority choice** — bundling `org.dmfs.tasks` makes Agendula a *replacement*
for OpenTasks (one authority owner per device). Intended (one app instead of
two), but a conscious choice.
5. **Repo home** — sibling Gitea repo next to Calendula, MIT license, same CI.
---
## 10. Sync sources Floret inherits for free (README copy)
## 10. Sync sources Agendula inherits for free (README copy)
Because Floret builds on the provider, not on any one sync app, it works with
Because Agendula builds on the provider, not on any one sync app, it works with
**anything that writes to the tasks provider**: DAVx5 (CalDAV), SmoothSync,
CalDAV-Sync, DecSync CC, and any Android sync adapter — no per-app integration.
Google Tasks / Microsoft To Do are out of scope by design (proprietary, would

View File

@@ -1,6 +1,6 @@
# Floret — documentation
# Agendula — documentation
Floret is a Material 3 Expressive **task** app for Android: a pure front-end over
Agendula is a Material 3 Expressive **task** app for Android: a pure front-end over
the OpenTasks `TaskContract` provider (synced by DAVx5 / SmoothSync / DecSync
over CalDAV), with no own database or sync stack. Sibling to
[Calendula](https://gitea.jeanlucmakiola.de/makiolaj/calendula). See the
@@ -10,7 +10,7 @@ top-level [`../README.md`](../README.md) for the project pitch.
| Doc | What it covers |
|---|---|
| [`ARCHITECTURE.md`](ARCHITECTURE.md) | How Floret is built **today** — layers, the data seam, provider resolution, the reminder engine, DI, build/tooling, manifest. Start here to work on the code. |
| [`ARCHITECTURE.md`](ARCHITECTURE.md) | How Agendula is built **today** — layers, the data seam, provider resolution, the reminder engine, DI, build/tooling, manifest. Start here to work on the code. |
| [`ROADMAP.md`](ROADMAP.md) | **Status** and what's next — milestones (M0M6 + Posture B), what's done, open decisions, how to build/verify. |
| [`PLAN.md`](PLAN.md) | The original implementation plan and **design rationale** — the A-now-B-later thesis, what transfers from Calendula, the locked decisions. The "why". |
| [`RELEASING.md`](RELEASING.md) | How to cut a release — the git-tag-as-source-of-truth flow, CI jobs, F-Droid repo, required secrets. |

View File

@@ -1,6 +1,6 @@
# Floret — releasing
# Agendula — releasing
Floret is distributed through a **self-hosted F-Droid repo** (on Hetzner) with a
Agendula is distributed through a **self-hosted F-Droid repo** (on Hetzner) with a
human-readable **Gitea release** per tag. Both are produced automatically by
`.gitea/workflows/release.yaml` when you push a tag. There are no APK assets on
the Gitea release itself — distribution lives in the F-Droid repo; the release is
@@ -94,7 +94,7 @@ Keep this green before tagging.
## F-Droid metadata
App store listing lives in `fdroid-metadata/` (`de.jeanlucmakiola.floret.yml`
App store listing lives in `fdroid-metadata/` (`de.jeanlucmakiola.agendula.yml`
plus `en-US/` summary/description). Per-version changelogs are generated into the
repo's `metadata/.../en-US/changelogs/<versionCode>.txt` from `CHANGELOG.md` at
release time; metadata is uploaded alongside `repo/` so changelog history

View File

@@ -1,4 +1,4 @@
# Floret — roadmap
# Agendula — roadmap
Where the project is and where it's going. This is the **status** view; the
design rationale behind each milestone lives in [`PLAN.md`](PLAN.md), and how the
@@ -50,7 +50,7 @@ The real Material 3 Expressive UI, built screen by screen against the M1
ViewModels.
- ✅ Provider/permission onboarding gate (`RootScreen`).
- ✅ Lists overview (`ListsScreen`) — smart lists + user lists grouped by account.
- ✅ Navigation host wiring (`FloretNavHost` + `Dest` route table: lists → task
- ✅ Navigation host wiring (`AgendulaNavHost` + `Dest` route table: lists → task
list → detail / edit, each binding its M1 ViewModel from route args).
- ✅ Task list screen — checkbox rows + toggle-complete, swipe-to-complete /
-delete (`SwipeToDismissBox`), inline add field (`InlineAdd``quickAdd`),
@@ -140,12 +140,12 @@ permissions; ship self-contained. UI / repository / domain untouched — see
These carry over from [`PLAN.md`](PLAN.md) §9; resolved ones are struck through.
1. ~~**Name** — `Floret`~~ confirmed (appId `de.jeanlucmakiola.floret`).
1. ~~**Name** — `Agendula`~~ confirmed (appId `de.jeanlucmakiola.agendula`).
2. ~~**tasks.org provider authority**~~ verified on device:
`org.tasks.opentasks` + `org.tasks.permission.*`.
3. **jtx Board** — support its richer contract later, or stay OpenTasks-only?
(Not in the candidate list today.)
4. **Posture B authority choice** — bundling `org.dmfs.tasks` makes Floret a
4. **Posture B authority choice** — bundling `org.dmfs.tasks` makes Agendula a
*replacement* for OpenTasks (one authority owner per device). Intended, but a
conscious choice.
5. **Recurring tasks** — read as occurrences today (`isRecurring` flag exists);