# 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 pieces fit together is in [`ARCHITECTURE.md`](ARCHITECTURE.md). Status legend: โœ… done ยท ๐Ÿšง in progress ยท โฌœ not started --- ## Current state (one line) The full non-visual stack ("backoffice") over the OpenTasks `TaskContract` provider is **done and unit-tested**, and the Material 3 Expressive UI is built through **M5**: lists โ†’ task list (swipe gestures, inline add, smart-list section headers) โ†’ detail / edit with full CRUD, date-time pickers, priority, percent-complete, conflict-safe saves, per-task reminders, and subtask create + reparent โ€” plus a one-time reminder onboarding step and a Settings screen (theme, dynamic colour, due-reminder master toggle + default offset + exact-alarm status, default list, and the add-a-subtask-row opt-out). Remaining work is M6 (Glance widget, translations, F-Droid release; the Settings screen landed early with M5 and still needs a language entry). --- ## Milestones ### โœ… M0 โ€” Skeleton Project scaffolding copied from Calendula (Gradle, version catalog, Hilt, Material 3 Expressive theme, Gitea CI/release workflows, F-Droid metadata), reseeded to a warm-mauve fallback palette. Builds, shows a themed placeholder. ### โœ… M1 โ€” Read path + logic (the "backoffice") The complete non-visual stack: - Vendored `TaskContract` subset, `ProviderResolver` (runtime authority detection), `ColumnReader`, mappers, `AndroidTasksDataSource` (Instances query + `ContentObserver`), and `TasksRepository` exposing live Flows of lists / tasks / detail. - Domain models, smart-list filtering (Today / Upcoming / Overdue / No-date / All / Completed), sorting, form validation, subtasks via `parentId`. - Self-scheduled due-reminder engine (AlarmManager + boot / provider-change re-sync), notifications, DataStore prefs. - Render-only ViewModels + UiState for **every** screen, so the UI is build-only from here. - JVM unit tests across mappers, filtering, sorting, form, value mapping, and day windows. ### โœ… M2 โ€” Screens: lists, task list, complete & CRUD 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 (`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`), smart-list section headers (`TaskSections`). - โœ… Detail + edit screens โ€” detail shows title / description / subtasks with edit+delete; edit has the full CRUD form (title, description, save) wired to a list picker. ### โœ… M3 โ€” Detail / edit polish - โœ… Due / start date-time pickers (`DateTimePickerFlow` in `TaskEditScreen`). - โœ… Priority โ€” coloured by level: green / amber / red pastels (`priorityFill`; M3 has no priority role, only `error`). A shared `ui/common/PriorityChip` on the list and detail screens, and the edit form's M3 segmented selector tints its active segment in the chosen level's hue. - โœ… Smart-list section presentation (`TaskSections` headers on the task list). - โœ… Percent-complete field โ€” optional "Progress" slider (5% detents) on the edit form; writes `Tasks.PERCENT_COMPLETE` (clamped 0โ€“100, status left to the complete toggle). - โœ… Conflict-safe saves โ€” `updateTask` re-checks `last_modified` against the value captured when the form loaded and throws `TaskConflictException`; the editor offers overwrite-or-cancel instead of clobbering an external change. ### โœ… M4 โ€” Subtasks (UI) `RELATED-TO` hierarchy in the UI. The data layer already reads `parentId`. - โœ… Create subtask (`AddSubtaskField` โ†’ `TaskDetailViewModel.addSubtask` sets `parentId`); subtasks render as a grouped section in the detail screen. Tapping a subtask opens its own detail (a new `TaskDetail` entry, so it can have children too), and the subtask's detail shows a tappable "Part of โ€ฆ" parent card so it never reads as a stray standalone task. - โœ… Inline expansion on the task list โ€” a parent row has a dedicated expand button (trailing chevron, separate from the count chip). Each section flattens into one grouped run (parents + their expanded children) and corners are chosen per-edge (`cornerPosition`): top-level tasks keep their own run, an expanded parent opens its bottom to its children, and the child group rounds off on its last segment while the next top-level task stays mid-run. Children are full-width, set a step down in tone (`surfaceContainer` vs the parents' `surfaceContainerHigh`) and with no colour bar (checkbox stays aligned). An expanded group ends with an inline "add a subtask" row (on by default; opt out in Settings โ†’ Tasks since M5). Offered only where the list holds all the children (a real list; smart lists that omit off-day children stay collapsed). `TaskDetail.parent` carries the parent for the detail card. - โœ… Reparent โ€” a full-width, searchable "Parent task" sheet on the edit form groups active candidates by due-date section (Overdue / Today / Upcoming / No date) and files a task under any top-level task in its list (or "None" to promote it); switching list clears the now-invalid parent. Candidates stay active + top-level to keep nesting one level deep, matching the detail screen. ### โœ… M5 โ€” Reminders onboarding & polish The engine exists (M1: `ReminderScheduler` + boot / provider-change re-sync, `DueReminderReceiver`, `TaskNotifier`). - โœ… Per-task reminder-offset UI (`ReminderPickerDialog` โ†’ `TaskForm` `reminderMinutesBeforeDue`). - โœ… `POST_NOTIFICATIONS` onboarding flow โ€” a one-time `ReminderOnboardingScreen` (Calendula's shell + copy adapted for tasks) gated in `RootScreen` after the provider/permission grant; it requests the runtime permission (API 33+) and records the choice (`reminderOnboardingDone`). Re-requestable from Settings. - โœ… Exact-alarm surface โ€” a status row in Settings โ†’ Reminders, shown only on Android 12 (where `SCHEDULE_EXACT_ALARM` is revocable), deep-linking to the system grant screen; on 13+ the app holds `USE_EXACT_ALARM` (always granted). - โœ… Default reminder-offset settings UI โ€” exposed as "When to remind" in Settings โ†’ Reminders (`reminderLeadMinutes`), behind a master `remindersEnabled` switch that gates the entire engine (`ReminderScheduler` clears all alarms when off; `DueReminderReceiver` suppresses any in-flight fire). - โฌœ End-to-end verification on device (build + unit tests green; not yet run on a device with a live provider). ### ๐Ÿšง M6 โ€” Settings, widget, i18n, release - โœ… F-Droid metadata scaffolded (`fdroid-metadata/`). - โœ… Settings screen โ€” landed early with M5 (`SettingsScreen` in the nav graph, reached by the gear on the lists overview). Covers theme, dynamic colour, due reminders (toggle + default offset + exact-alarm status), default list, and the add-a-subtask-row opt-out. Still โฌœ a **language** entry (deferred until there are translations to switch to). - โฌœ Glance task-list widget โ€” deps present in `build.gradle.kts`, zero impl. - โฌœ Translations โ€” only `res/values/` (English); no `values-XX`. - โฌœ Finalize F-Droid metadata, confirm CI release flow. ### โฌœ Posture B (separate track, later) Add a `:provider` module bundling the Apache-2.0 `opentasks-provider`; `ProviderResolver` defaults to our own `org.dmfs.tasks`; add sync-adapter permissions; ship self-contained. UI / repository / domain untouched โ€” see [`ARCHITECTURE.md`](ARCHITECTURE.md) ยง7. --- ## Open decisions / to verify These carry over from [`PLAN.md`](PLAN.md) ยง9; resolved ones are struck through. 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 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); recurrence-aware editing is out of scope for v1. --- ## How to contribute / verify - Build: `./gradlew :app:assembleDebug` - Unit tests: `./gradlew :app:testDebugUnitTest` - Run on a device/emulator that has **OpenTasks** or **tasks.org** installed (and ideally DAVx5 syncing a CalDAV task list) so the read/write paths have real data. Debug builds use `DemoSeeder` for sample data when no provider data is present.