Files
agendula/README.md
Jean-Luc Makiola 1ed192f150 feat(store)!: delete the vendored dmfs provider
Phase 5 of docs/OWN-STORE.md. The :provider module goes — 84 Java files,
14,555 lines, its <provider>, its two custom permissions, its 13
translated strings and its three dmfs runtime dependencies. Room has been
the default since the previous commit and every v0.3.x install has been
imported, so nothing reads it any more.

StorageMode.LOCAL is gone with it; OWN and EXTERNAL are what remain.
ProviderResolver narrows to what it was always really for — discovering
external providers — and answers null in OWN mode, where there is no
authority to resolve. Callers that need to tell that apart from "External
with nothing installed" ask mode(). ProviderStatus is unconditionally
READY in OWN mode: the permission gate only ever applied to External, and
that is now visibly true rather than a special case inside it.

A stored LOCAL is read as OWN rather than as an unparseable value. Left
to fall through to autoMode, someone who had explicitly chosen local
storage while also having OpenTasks granted would have been sent to
OpenTasks instead.

ProviderChangeReceiver's manifest filter drops our own authority — safe
now, because nothing of ours broadcasts ACTION_PROVIDER_CHANGED. In OWN
mode Room's InvalidationTracker covers foreground changes and nothing
outside the app can change our data. When SYNC.md phase 3 lands, the sync
worker must call ReminderScheduler.sync() itself; that is the replacement
for the broadcast and it belongs in the sync work.

lib-recur stays as a direct dependency and is still Apache-2.0 dmfs, so
the attribution is still owed — now as a normal third-party dependency.
provider/PROVENANCE.md is replaced by a postscript in STORAGE-DECISION.md
recording that the fork existed, why, and the one detail that still binds
us: tasks.org is DB 22 and has no is_recurring, so TaskMapper must keep
deriving recurrence from rrule/rdate.

BREAKING: the de.jeanlucmakiola.agendula.tasks authority and both custom
permissions are gone. Anyone who pointed DAVx5 or another app at that
authority loses it; External mode is the answer. Needs calling out in the
release notes.

Verified: the APK declares no ContentProvider, no custom permission and
no agendula.tasks authority, and carries no dmfs provider classes.
2026-08-13 16:33:19 +02:00

81 lines
4.1 KiB
Markdown

<div align="center">
<h1>Agendula</h1>
<p><strong>A modern Material 3 Expressive task app for Android.</strong><br>
Keeps your tasks on your device, or on top of a tasks provider you already use.
Open standards, no account required.</p>
<a href="https://codeberg.org/jlmakiola/agendula/actions"><img src="https://codeberg.org/jlmakiola/agendula/actions/workflows/ci.yaml/badge.svg?branch=main" alt="CI"></a>
<img src="https://img.shields.io/badge/Android-10%2B-3DDC84?logo=android&logoColor=white" alt="Android 10+">
<img src="https://img.shields.io/badge/Kotlin-Compose-7F52FF?logo=kotlin&logoColor=white" alt="Kotlin + Compose">
<img src="https://img.shields.io/badge/Material%203-Expressive-4285F4" alt="Material 3 Expressive">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
</div>
Agendula is the task-list sibling to [Calendula](https://codeberg.org/jlmakiola/calendula).
Where Calendula is a pure front-end over Android's `CalendarContract`, Agendula
keeps its own store, designed around RFC 5545's `VTODO` — the same tasks DAVx5
(and SmoothSync, DecSync, …) sync out of your CalDAV server. It can also read and
write a tasks provider you already have, for anyone already syncing that way.
The name rhymes with its sibling on purpose: **Agendula** is *agenda* — Latin for
“things to be done” — given Calendula's `-ula` ending. Calendula keeps your days;
Agendula keeps your to-dos. (A Calendula flower head is botanically a cluster of
many small *florets* — so the two apps are florets of one bloom.)
## Where your tasks live — your choice
| | Where | Sync | Needs |
|---|---|---|---|
| **On your device** *(default)* | Agendula's own database | none yet — CalDAV sync of our own is planned | nothing. No account, no permissions, no other app |
| **In a provider you already use** | OpenTasks or tasks.org | whatever syncs it for you — DAVx5 and friends | that app installed, and its read/write permission |
Agendula's own store is an ordinary app database — nothing is published to other
apps, so there is no authority to clash over and no permission to grant. It
**coexists with OpenTasks rather than replacing it**: installing one never breaks
the other, and if you already sync through a provider, that keeps working exactly
as it did.
Recurring tasks are expanded per RFC 5545, and everything the schema does not
model is round-tripped verbatim rather than dropped — so passing your tasks
through Agendula does not quietly lose fields a server sent.
Your tasks are exportable as standard iCalendar `.ics` files at any time, because
data you can't take with you isn't really yours.
> **Status: backend complete, UI catching up.** Storage, reads and
> writes, smart-list filtering, a self-scheduled reminder engine, and export are
> built and unit-tested. The Material 3 Expressive screens are being built on
> top, one at a time — the storage-mode picker and export screen are not there
> yet. See [`docs/ROADMAP.md`](docs/ROADMAP.md) for status,
> [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for how it's built, and
> [`docs/STORAGE-AND-SYNC.md`](docs/STORAGE-AND-SYNC.md) for why storage works
> the way it does.
## Sync sources (by design)
In external-provider mode Agendula works with anything that writes to that provider —
**DAVx5** (CalDAV), **SmoothSync**, **CalDAV-Sync**, **DecSync CC**, or any
Android sync adapter — because it builds on the provider, not on any one sync
app. Google Tasks / Microsoft To Do are out of scope by design (proprietary; they
would mean owning a sync stack). Open standards — CalDAV / iCalendar / DecSync —
are the lane.
## Translations
Agendula ships in English so far, and would like not to. Translations are
managed on a self-hosted **Weblate**, and partial ones are fine — an
untranslated string simply falls back to English.
**→ [Help translate Agendula](https://weblate.dev.jeanlucmakiola.de/engage/agendula/)**
No coding needed: register on the Weblate server, pick (or request) a language,
and translate the strings in your browser. You can also reach this link in the
app from the top of **Settings → App language**.
## License
MIT — see [LICENSE](LICENSE).