Files
agendula/docs/ROADMAP.md
Jean-Luc Makiola e0c56a73e2
All checks were successful
CI / ci (push) Successful in 6m49s
M4: percent-complete, conflict-safe saves & subtask reparent
M3 detail/edit polish:
- "Progress" slider on the edit form writes Tasks.PERCENT_COMPLETE (clamped
  0-100, 5% detents); status stays owned by the complete toggle.
- Conflict-safe saves: updateTask re-checks the provider's 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):
- Reparent: a "Parent task" picker files a task under any top-level task in its
  list (or "None" to promote it); candidates stay top-level to keep nesting one
  level deep. Switching list clears the now-invalid parent.
- Tapping a subtask opens its own detail (a new TaskDetail entry, own VM).

Tests: percent clamping, parent-id write, populatedFields reveal logic.
Docs: ROADMAP M2/M3/M4 reconciled to the codebase; CHANGELOG [Unreleased].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 21:15:15 +02:00

8.0 KiB
Raw Blame History

Floret — 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, and how the pieces fit together is in 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 M4: 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. Remaining work is M5 (notification / exact-alarm onboarding, default reminder UI) and M6 (Settings screen, Glance widget, i18n, release).


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 (FloretNavHost + 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 (InlineAddquickAdd), 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 0100, 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 (AddSubtaskFieldTaskDetailViewModel.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 (always on for now; an opt-out toggle lands with the M6 settings screen). 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 (ReminderPickerDialogTaskForm reminderMinutesBeforeDue).
  • POST_NOTIFICATIONS + exact-alarm onboarding flow — currently only graceful runtime checks (TaskNotifier.canPost, canScheduleExactAlarms), no user-facing gate.
  • Default reminder-offset settings UI — SettingsPrefs/SettingsViewModel back it, but no Composable exposes it.
  • End-to-end verification on device.

🚧 M6 — Settings, widget, i18n, release

  • F-Droid metadata scaffolded (fdroid-metadata/).
  • Settings screen — SettingsViewModel exists but no SettingsScreen Composable and it is not in the nav graph (only used by MainActivity for app-level theming). Needs theme / dynamic-color / language, default list, default reminder, and the opt-out toggle for the task list's inline add-a-subtask row (on by default since M4).
  • 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 §7.


Open decisions / to verify

These carry over from PLAN.md §9; resolved ones are struck through.

  1. NameFloret confirmed (appId de.jeanlucmakiola.floret).
  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 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.