Let other apps write to the own store through the provider (#63)

This commit is contained in:
makiolaj committed 2026-10-15 14:25:23 +02:00
1 parent e7bf23e276
commit 522d5ca35e
22 files changed
+2250 -66

No files matched your search

+72 -13
View File
@@ -1,21 +1,23 @@
# Reading Agendula's tasks from another app
# Using 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
publishes them through a `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.
An app that already works with OpenTasks or tasks.org can work with Agendula by
using a different authority and permissions.
| | |
|---|---|
| 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 |
| Read permission | `de.jeanlucmakiola.agendula.permission.READ_TASKS` (offline build: `de.jeanlucmakiola.agendula.offline.permission.READ_TASKS`) |
| Write permission | `de.jeanlucmakiola.agendula.permission.WRITE_TASKS` (offline build: `de.jeanlucmakiola.agendula.offline.permission.WRITE_TASKS`) |
| Protection level | `dangerous`: request them 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`.
`<package>.tasks` and the permissions are `<package>.permission.READ_TASKS` and
`<package>.permission.WRITE_TASKS`.
| Build | Package |
|---|---|
@@ -40,15 +42,72 @@ 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.
## Writing
Writes need the write permission and go through the same code as edits made in
Agendula itself: a changed task is marked for the next CalDAV sync, reminders
are re-armed, and widgets refresh.
| Table | insert | update | delete |
|---|---|---|---|
| `tasklists` | no | no | no |
| `tasks` | yes | yes | yes |
| `instances` | no | see below | one occurrence |
| `properties` | alarms and tags | alarms and tags | alarms and tags |
`update` and `delete` take a `selection` like a query does, and it matches the
rows you can read. `insert` returns the new row's URI. `applyBatch` runs in one
transaction: if any operation fails, none of them is applied. A write that
changes nothing does not mark the task for sync.
**Columns.** A task write may set `list_id`, `_uid` (on insert only), `title`,
`description`, `location`, `url`, `priority`, `class`, `status`,
`percent_complete`, `completed`, `task_color`, `dtstart`, `due`, `duration`,
`is_allday`, `tz`, `rrule`, `rdate`, `exdate`, `parent_id` and `sorting`. Other
columns the table publishes are ignored, so you can write back a row you read.
A column the table does not have is rejected with `IllegalArgumentException`.
Agendula's sync state (the server's URL and ETag, the iCalendar `SEQUENCE`, and
properties it does not understand) is not published and cannot be written.
**Status.** `status`, `percent_complete` and `completed` stay consistent the way
OpenTasks keeps them. Completing a task sets 100 % and a completion time,
progress between 1 and 99 sets "in process", and a completion time alone
completes the task.
**Recurring tasks.** Writing `status`, `percent_complete` or `completed` to an
`instances` row completes, cancels or reopens that one occurrence; partial
progress is rejected. Other content columns written to an instance change the
whole series, and that occurrence too if it had been modified on its own.
Changing a time or recurrence column (`dtstart`, `due`, `duration`,
`is_allday`, `tz`, `rrule`, `rdate`, `exdate`) is rejected on an instance of a
recurring task, so move a series through `tasks`; writing those columns back
unchanged, as in a row you read, is fine. Moving a series' start or changing its
rule drops the occurrences that had been modified on their own, as it does in
the app. Deleting an `instances` row deletes that one occurrence.
**Subtasks.** A subtask has to be in its parent's list. Moving a task to another
list moves its subtasks with it, and deleting a task deletes them. A task in a
synced list stays as a hidden tombstone until the next sync removes it from the
server.
**Rejected writes** throw `IllegalArgumentException` for a bad value, a missing
list or parent, or a parent in another list. They throw
`UnsupportedOperationException` for lists and for tasks in a list the server
has made read-only, and `IllegalStateException` while Agendula works on top of
an external provider or is still starting. `list_access_level` does not tell
you which lists are read-only: OpenTasks reserves it and asks providers to
publish `0`.
## 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`.
OpenTasks or tasks.org, every table is empty and writes are refused. Use that
provider directly.
- **Ids are stable.** A task keeps its `_id`. An instance's `_id` is built
from its series and its original time, and a property's from its alarm or
its task and tag, so they stay the same across reads and within a batch for
as long as that occurrence, alarm or tag exists. `distance_from_current`
still moves with the clock, and observers are notified hourly when it does.
+16 -8
View File
@@ -55,7 +55,8 @@ All of this is verifiable in the
- **On your device (the default)** — your task lists, tasks and reminders are
kept in Agendula's own database inside the app's private storage, and
uninstalling the app removes it. Another app can read these tasks only if you
grant it Agendula's "read Agendula tasks" permission (see section 9).
grant it Agendula's "read Agendula tasks" permission, and change them only if
you grant it "add, change and delete Agendula tasks" (see section 9).
- **In a tasks provider you already use** — OpenTasks or tasks.org. Agendula
then reads and writes that app's task database through Android's provider
mechanism, after you grant its read/write permission. Whatever already
@@ -210,13 +211,20 @@ source:
used for scheduled background work (WorkManager); on older Android versions
it needs them to run an expedited sync.
Agendula declares one permission that other apps can request:
`de.jeanlucmakiola.agendula.permission.READ_TASKS`. Android asks you before
granting it, and you can revoke it at any time in the system settings. An app
that holds it can read your task lists, tasks, reminders and tags from
Agendula's own database (read-only, and nothing while Agendula works on top of
an external tasks provider). What that app then does with them is governed by
its own privacy policy.
Agendula declares two permissions that other apps can request. Android asks you
before granting either, and you can revoke them at any time in the system
settings.
- `de.jeanlucmakiola.agendula.permission.READ_TASKS`: an app that holds it can
read your task lists, tasks, reminders and tags from Agendula's own database.
- `de.jeanlucmakiola.agendula.permission.WRITE_TASKS`: an app that holds it can
add, change, complete and delete those tasks, reminders and tags. It cannot
change or delete task lists. Its changes sync to your CalDAV server like your
own edits.
Neither gives access to anything while Agendula works on top of an external
tasks provider. What an app does with your tasks is governed by its own privacy
policy.
## 10. Distribution channels