In Own storage mode, Agendula now publishes its Room store as a ContentProvider that follows the OpenTasks contract. It serves `tasklists`, `tasks`, `instances` and `properties` (reminders and tags) at `<package>.tasks`, behind a dangerous-level `<package>.permission.READ_TASKS` that the user grants per app. An app that already reads OpenTasks or tasks.org only has to change the authority and the permission. On a query, the provider lays the Room store out in OpenTasks' columns and loads that into an in-memory SQLite database, so a client's selection, arguments and sort order run as real SQL. The snapshot is rebuilt after a write, after a storage mode switch, or once it is an hour old, because instance ids and `distance_from_current` move with the clock. Observers on the authority get one notification per burst of changes. In External mode every table is empty. Recurrence expansion moved out of `RoomTasksDataSource` into shared functions, so the app and the provider expand a series the same way. `docs/CONTENT_PROVIDER.md` describes the contract for client developers, with the authority and permission for each build. README, CHANGELOG and the privacy policy mention the new permission. Where this differs from the issue: the issue asked for read and write permissions, and this PR is read-only. Insert, update and delete throw. Accepting writes from other apps means deciding what happens to the fields Agendula keeps verbatim for CalDAV round-tripping, which was the issue's second open question, and that needs its own design. That is tracked in #63. Closes #43 Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/62
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# Reading Agendula's tasks from another app
|
||||
|
||||
When Agendula keeps tasks in its own database (the default storage mode), it
|
||||
publishes them through a read-only `ContentProvider` that follows the
|
||||
[OpenTasks contract](https://github.com/dmfs/opentasks/blob/master/opentasks-contract/src/main/java/org/dmfs/tasks/contract/TaskContract.java).
|
||||
An app that already reads OpenTasks or tasks.org can read Agendula by using a
|
||||
different authority and permission.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Authority | `de.jeanlucmakiola.agendula.tasks` (offline build: `de.jeanlucmakiola.agendula.offline.tasks`) |
|
||||
| Permission | `de.jeanlucmakiola.agendula.permission.READ_TASKS` (offline build: `de.jeanlucmakiola.agendula.offline.permission.READ_TASKS`) |
|
||||
| Protection level | `dangerous`: request it at runtime |
|
||||
| Package visibility | add `<provider android:authorities="de.jeanlucmakiola.agendula.tasks" />` to your `<queries>` |
|
||||
|
||||
Both names are derived from the installed package name, so every build has its
|
||||
own and they can be installed side by side: the authority is
|
||||
`<package>.tasks` and the permission `<package>.permission.READ_TASKS`.
|
||||
|
||||
| Build | Package |
|
||||
|---|---|
|
||||
| Release | `de.jeanlucmakiola.agendula` |
|
||||
| Release, offline | `de.jeanlucmakiola.agendula.offline` |
|
||||
| Debug | `de.jeanlucmakiola.agendula.debug` |
|
||||
| Debug, offline | `de.jeanlucmakiola.agendula.offline.debug` |
|
||||
| Release test | `de.jeanlucmakiola.agendula.releasetest` |
|
||||
| Release test, offline | `de.jeanlucmakiola.agendula.offline.releasetest` |
|
||||
|
||||
## Tables
|
||||
|
||||
| URI | Rows |
|
||||
|---|---|
|
||||
| `content://<authority>/tasklists` | one per list |
|
||||
| `content://<authority>/tasks` | one per task; a modified occurrence of a recurring task is its own row with `original_instance_id` / `original_instance_time` set |
|
||||
| `content://<authority>/instances` | one per occurrence: recurring tasks are expanded up to one year back and two years ahead, and modified occurrences replace the ones they override |
|
||||
| `content://<authority>/properties` | reminders (`vnd.android.cursor.item/alarm`) and tags (`vnd.android.cursor.item/category`) |
|
||||
|
||||
Each table also accepts `/<id>`. Column names, value encodings (epoch millis,
|
||||
all-day dates as UTC midnight, `status`, `priority`, `class`) and MIME types
|
||||
are the OpenTasks ones. `selection`, `selectionArgs` and `sortOrder` work as
|
||||
they do against OpenTasks.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- **Read-only.** `insert`, `update` and `delete` throw
|
||||
`UnsupportedOperationException`.
|
||||
- **Change notifications.** Register a `ContentObserver` on
|
||||
`content://<authority>` with `notifyForDescendants = true`. It fires when
|
||||
tasks change or when the user switches storage mode.
|
||||
- **External storage mode.** When the user has Agendula work on top of
|
||||
OpenTasks or tasks.org, every table is empty. Read that provider directly.
|
||||
- **Instance ids** are not stable: as in OpenTasks they are reassigned when the
|
||||
data changes, and also hourly as the expansion window moves (observers are
|
||||
notified then too). Key on `task_id` plus `instance_original_time`.
|
||||
Reference in New Issue
Block a user