In Agendula's own storage, ticking off the current occurrence of a repeating task now moves the series on to its next date and keeps a completed copy, as tasks.org and Apple Reminders do.
Before, the series stayed open and the completion was a `RECURRENCE-ID` override with `STATUS:COMPLETED` in the same resource. That's valid iCalendar, but Infomaniak's web view and Apple Reminders ignore it and keep showing the task as open, which is what #137 reports. The reporter hasn't confirmed their storage mode yet. With tasks.org/OpenTasks the provider already moves the series on, so this change is about the built-in CalDAV sync.
How it works (`RoomTasksDataSource`):
- Ticking or cancelling the series' **first open occurrence** moves the master to its next open occurrence. Closed overrides on either side become standalone completed copies (new UID, fresh alarm identities), including the ones 1.3.0 left behind. `COUNT` drops by the occurrences used up (counted before `EXDATE`, shared with `splitSeries`), `EXDATE` values before the new start are dropped, and status and progress are reset. An edited occurrence is converted in place, so its id, edits and own properties survive. Lines inside nested components, such as a `VALARM`'s `UID`, are kept.
- Series with `RDATE`s keep the override model, because moving the anchor onto an `RDATE` would re-align the `RRULE`. So do recurring subtasks, whose copies would otherwise pile up under the parent.
- How the series moved is recorded in two new local tables, `advanced_occurrences` and `advanced_detached` (schema v5, migration and migration test included). They are never part of the synced `VTODO`. A record keeps mapping the occurrence to its copy (pruned after 30 days), so the detail screen and reminders find the copy even after it was edited. Every save path goes through one check: when the only thing that changes on a copy is that it reopens (compared as the editor writes values back), the move is undone. That covers undo, the completed section, the editor, the detail screen and other apps through Agendula's provider. The anchor goes back, `COUNT` regains what it spent, the dropped `EXDATE`s return in the list's own value shape, other edits made since are kept, and a reopened state such as 40% stays on the occurrence. Detached overrides come back as overrides; one deleted since, or given its own parent or subtasks, is excluded instead. A copy that was edited, moved or given subtasks, or whose series moved on, changed its rule or was deleted, reopens as a task of its own.
- If a sync discards the local version of a moved series because the server's is newer, the copies of moves the server never saw are deleted too, and the server's own overrides stand for those occurrences. Moves already uploaded are marked settled and left alone.
- `task(series, anchor)` resolves a moved-past occurrence to its copy, so the detail screen and reminder checks see it as done. The repository then acts on the row it resolved, including the detail screen's subtasks, so deleting or saving from a screen opened before the move can't hit the whole series.
- A stale tick (an old notification or widget row) for an occurrence the series has already moved past does nothing.
- Ticking the last open occurrence closes the series, and reopening any of its occurrences reopens the series.
- Any other occurrence (with earlier ones still open) keeps the override model.
Also fixed here: `AgendulaTaskProviderTest` failed on `release/v1.3.1` whenever Agendula ran on an external provider, because `ProviderWriter` checked the active store first. Read-only tables are now refused as such whichever store is active (one exhaustive `when` per operation), and the two tests that check value errors skip when the provider takes no writes at all.
Tests: `RoomTasksDataSourceTest` covers each case above. There is also a migration test, a `ProviderWriterTest` for reopening through the provider, and `RoomSyncStoreAdvancesTest` (full flavour) for the sync discard. The full on-device suite (223 tests) passes on the emulator, and the unit tests pass for both flavours.
Closes #137
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/144
Stable 1.3.0, after 1.3.0-beta.1 and beta.2.
- Version bumped to 1.3.0 (1030099); the CHANGELOG entries moved from Unreleased to 1.3.0.
- What's New for every shipped locale, now including ru-RU, which also gets its full store listing. values-uk and values-zh-rCN came in empty from Weblate, so they get no store locale yet.
- fr, ar, cs and ru added to locales_config.xml, so they show up in the in-app language picker and Android's per-app language settings. Before, only en, de and pt-BR did.
- `main` (Weblate #87) merged in, no conflicts and no stale keys.
- Lint has failed on this branch since #113: `NavController.currentBackStack` is a RestrictedApi. It's suppressed at that one call, since no public API exposes the whole back stack and `visibleEntries` drops an entry once another one covers it.
#119, #121, #122/#123 and #127/#129 landed after beta.2, so no tester has seen them yet.
Closes #68, closes #73, closes #90, closes #91, closes #93, closes #94, closes #99, closes #104, closes #105, closes #119, closes #121, closes #122, closes #123, closes #127, closes #129
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/134
Stable 1.2.0, after 1.2.0-beta.1 and beta.2.
- Version bumped to 1.2.0 (1020099); the CHANGELOG entries moved from Unreleased to 1.2.0.
- What's New for every shipped locale, now including cs-CZ.
- Full store listing (title, short and full description) for every locale; new for cs-CZ, ar and fr-FR. Play only gets these through the `listing` lane.
- `main` is merged in with the branch side kept. #81 and the Weblate updates already came in through the #88 squash, and the only lines unique to `main` were string keys that #88 removed.
The #75 fix landed after beta.2, so no reporter has tested it yet.
Closes #72, closes #78, closes #75, closes #92
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/103
Reminders set in Agendula never reached the server, so a Mac or any other client saw none, and alarms set there never rang here. Reminders now sync as VALARMs in both directions.
**What syncs**
- Alarms Agendula can fire exactly as written become reminders: DISPLAY/AUDIO, relative to start or due, or absolute on a non-recurring task.
- Everything else stays in the residue and round-trips untouched: Apple default alarms, location alarms, repeating alarms, RFC 9074 snoozes, and triggers after their anchor.
- An unchanged alarm is re-emitted verbatim. An edited one is authored fresh under its existing uid. All-day leads go out as whole days.
- `ACKNOWLEDGED` is only added once a reminder is past the planner's missed-reminder grace period.
**Behaviour changes**
- Default reminders are stored on the task when it first gets a due date, so other clients see them. While untouched, they swap with the other default when the task flips between timed and all-day.
- Removing a task's alarms in another app turns them off here instead of falling back to the default. Tasks from clients that never write alarms keep the default.
- "All occurrences" reminder edits now reach occurrences that still had the series' old reminders.
- `.ics` import keeps VALARMs as reminders.
**Database v3**
- Alarms gain a `uid` and `raw` VALARM. Tasks gain `reminders_off`.
- The migration lifts claimable alarms out of the residue. It queues a one-time upload for tasks with local-only reminders on writable, non-confidential lists.
**Testing**
- Unit tests: full 662, offline 489. All instrumented tests pass on the emulator, including the migration. Lint is clean for both flavours.
- Not covered: the repository has no unit-test harness, so storing defaults at creation and the timed/all-day swap have no automated tests. Nothing has been checked against a real Mac yet.
Closes #52
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/81
Agendula 1.1.0. Merging this triggers the release pipeline (F-Droid, Codeberg, Play) and mints the v1.1.0 tag.
- Tags, synced as CATEGORIES (#35)
- Saved filters, also usable in the Tasks widget (#47)
- Offline flavor without sync or network, published to Codeberg only (#39)
- New Today, Up next, Lists and Week widgets; widgets can show several lists
- Synced lists can be renamed and deleted
- All-day tasks no longer land a day off west of UTC in smart lists and filters
versionName 1.1.0 / versionCode 1010099. CHANGELOG has the 1.1.0 section, and What's New is written for en-US, en-GB, de-DE, pt-BR, plus new fr-FR and ar.
After the tag lands, fdroiddata!49998 needs a 1.1.0 build entry with gradle: full.
Closes #35, closes #39, closes #47
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/51
Moves Gradle jobs to the dedicated `gradle` runner (capacity 1, 8g per job) and the cheap gate/lint jobs to `light`. Two runners used to share the `docker` label, so two Gradle builds could run at once and the VPS ran out of memory. Only `runs-on` lines change.
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/48