Adds tags, stored as the standard `CATEGORIES` property so they stay compatible with Nextcloud Tasks, tasks.org and DAVx⁵.
## What changed
- **Storage and sync:** tags are a new `categories` column (database v2). The migration lifts `CATEGORIES` out of the stored unknown properties for tasks that were synced before, so existing tags show up without a re-download. A line Agendula can't write back exactly as it came (e.g. a duplicated value, or the property split across several lines) stays where it was and still syncs byte for byte.
- **Provider mode (OpenTasks / tasks.org):** tags are read and written as the provider's category properties. Writes are a single batch, so a refused tag can't leave half the tags behind or create a task twice.
- **Imports:** the copy from OpenTasks/tasks.org and the legacy one-shot import both bring tags across.
- **Editor:** a new optional Tags field. Type a tag and press Done or type a comma; tags already in use are suggested while typing, and text left in the field is still saved.
- **Display:** tags show as pills on the detail screen and in each row's info line in task lists.
- **Using tags:** the home screen has a Tags section listing every tag on an open task with its count. Tapping one opens that tag's tasks across all lists, and "New task" there starts with the tag set. Every task list's sort button is now a "Sort and filter" picker, where you can tick one or more tags to narrow the list.
## Also in here
- **Keyboard:** a focused input now scrolls fully above the keyboard, not just its text line. This uses floret-kit's new `revealOnFocus` (jlmakiola/floret-kit#12), and the pointer moves to floret-kit main.
- **Edit screen:** once the title scrolls out of view, it shows in the top bar.
- **main merged in:** the branch brings main's #40 and #44 (floret-kit on its shared main, Gradle 9.8), which `release/v1.1.0` doesn't have yet.
## Where this differs from the issue
- **Filtering by several tags** shows tasks that carry *all* of them, since ticking more tags is meant to narrow the list.
- **Tags are case-sensitive:** "Work" and "work" are two tags, matching how they are stored and synced.
- **The overview** lists tags on open tasks only, the same set the editor suggests.
- **Widgets** can't be set to a tag yet.
Closes #35
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/46
Brings the exact tag checks from #40 into the release branch. beta.yaml runs from the branch's own files, and the old prefix-matching lookup treated v1.1.0-beta.1 as a shipped v1.1.0, so the 1.1.0-beta.2 run was refused.
The one conflict was in publish_codeberg_release.sh: kept #40's stricter upload handling plus the offline APK assets from #41. scripts/release_gate.sh now reports cut=true for 1.1.0-beta.2.
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/42
The edit pencil was gated to local lists even though rename/delete already
worked end to end for synced collections. Read-only shares keep the icon
but show it disabled, with a hint on tap explaining why.
Refs #29
Reworks the home-screen widgets on top of #32.
- Tasks widget: agenda-style rows, a row-colour setting, an "All caught up" empty state
- Drops "At a glance" (it duplicated the Tasks widget) for four new widgets: Today (progress ring + today's tasks), Up next (the nearest due task, optionally from one list), Lists (adaptive shortcut grid), Week (day strip with counts)
- Ticking a task in any widget shows a done frame before the row goes away
- Shared Glance primitives live in floret-kit (`feat/glance-module`): progress ring, tiles, check button, header action pill, expressive shapes, done state
Today and Up next don't offer the transparent background, since their content isn't in containers and is unreadable on the wallpaper. No issue behind this one; it came out of reviewing the widgets on-device.
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/33
Widget config now multi-selects real lists (smart lists stay
single-select, mutually exclusive) and offers an optional "group by
list" sort that heads each list's tasks with its name.
Closes #25
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/32
### What this changes
Adds beta releases. Pushing a `release/*` branch whose committed `versionName` is `X.Y.Z-beta.N` makes the new `.gitea/workflows/beta.yaml` run the unit tests, build and sign the APK with the app key, and publish it as a **Codeberg pre-release** (APK + `.sha256`), plus a Gitea pre-release with the R8 mapping. F-Droid (self-hosted and official) and Play never get a beta; Obtainium only offers it with *Include prereleases* on.
- **`scripts/version_info.sh`** is the single source for `versionName` → `versionCode`, used by `release.yaml`, `beta.yaml`, the changelog sync and the store-listing check. From 1.1.0: `X*1000000 + Y*10000 + Z*100 + N` for betas (N = 1–98), `+ 99` for stable, so `1.1.0-beta.1` → `1010001`, `1.1.0` → `1010099`. All 1.0.x versions keep the legacy formula, so `release/v1.0.1` (code `10001`) stays valid.
- **Shared publish scripts:** `scripts/publish_codeberg_release.sh`, `scripts/publish_gitea_release.sh` and `scripts/release_notes.sh`, moved out of `release.yaml`. The stable path behaves as before.
- **Guards:**
- CI fails a PR whose committed `versionCode` doesn't match its `versionName`.
- CI fails a PR into `main` that carries a beta version.
- `release.yaml`'s `detect` refuses a beta on `main` as a backstop.
- `beta.yaml` refuses a beta of a version that has already shipped as stable.
- Betas get no store What's New file.
- **Docs:** "Cutting a beta" and the versionCode table in `docs/RELEASING.md`; a note on beta tags in `docs/fdroid-official/README.md`; how to opt in to betas in the README.
### Why
To ship a test build of an upcoming version (e.g. 1.1.0) to opted-in testers before the stable release, without it reaching F-Droid or Play users.
### How it was tested
- `scripts/version_info.sh` against stable, beta, legacy and invalid version names.
- Both publish scripts against a mock forge API: create, re-run (PATCH plus asset replacement), Codeberg's 500-then-retry path, and the skip when no token is set.
- A scratch copy with `1.1.0-beta.1` committed: the version check passes, the changelog sync and `check_store_listing.py --complete` pass without a What's New, the PR-into-main guard trips, and a wrong `versionCode` is rejected.
- `sync_changelog_to_fastlane.sh` and `check_store_listing.py` (with and without `--complete`) still pass on the current `1.0.0`.
- All three workflow files parse as YAML.
Not run on the real runners yet. The first beta push is the live test of `beta.yaml`, which assumes a mirrored branch push starts a workflow on Gitea, the same way pushes to `main` already do.
### Checklist
- [x] No `versionName` / `versionCode` bump
- [x] No `values-*/strings.xml` touched
- [x] `CHANGELOG.md` not updated: this is release infrastructure, not a user-visible change
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/38
Renovate only rebases while it runs, and it ran once a week, so merging one dependency PR left the others conflicted until the next Monday.
- `renovate.yml`: also run on push to `main` (mirror syncs fire push events, like `release.yaml`).
- `renovate.json5`: new PRs only in a Monday window (`* 4-6 * * 1`) around the weekly cron; on-merge runs only rebase conflicted PRs (`rebaseWhen: "conflicted"`).
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/36
Adds the five phone screenshots from `fastlane/metadata/android/en-US/images/phoneScreenshots/` to the README header, as a centered row beneath the badges.
The README references the fastlane files directly rather than copies, so the README and the store listings stay in sync. Each image has descriptive alt text.
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/24
First stable release. Merging this bumps versionName to 1.0.0 and triggers the release pipeline (F-Droid, Codeberg, Play).
**App**
- CalDAV sync built in, with Agendula's own task store; OpenTasks / tasks.org stay available and can be copied over in Settings → Storage
- repeating tasks, several reminders per task, lists managed in the app, iCalendar import/export, widget and Quick Settings tile
- a list can be kept out of the smart lists (#18) and gets its own notification channel (#17)
- duplicate a task with its subtasks (#16)
- HTML descriptions shown as plain text (#15)
- relative day words in reminder notifications (#14)
- asks for exact-alarm access instead of claiming USE_EXACT_ALARM, and re-arms reminders when that access changes
**Release plumbing**
- floret-kit bumped to v0.4.0; the old pin was never pushed, so a clean clone couldn't check out the submodule. 0.4.0 drops CrashConfig.issueTitle (crash issues are always filed in English)
- prebuilt .so files ship unstripped, so the build no longer depends on whether an NDK is installed; now checked by check_reproducible_release.sh
- official F-Droid recipe in docs/fdroid-official/, to submit to fdroiddata once v1.0.0 is tagged
- Google Play: fastlane uploads the AAB and every locale's What's New after the F-Droid release; a separate listing lane pushes text and graphics from the fastlane tree, which CI now checks against Play's limits
- store listing: title "Agendula: Tasks" in every locale, icon, feature graphic, screenshots and 1.0.0 changelogs in en-US, en-GB, de-DE and pt-BR
crash_report_issue_title is now unused but stays until Weblate removes the translated copies.
Closes #14, closes #15, closes #16, closes #17, closes #18
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/20
The policy had no copy in this repository — it existed only inside the Astro page on jeanlucmakiola.de. This file becomes the single copy: the website build checks this repo out beside itself and renders `docs/PRIVACY.md` through a content collection, so the published page and the app's own documentation cannot drift. Same arrangement as calendula#293.
Shaped as the content entry the site expects: `title` / `description` / `updated` frontmatter, an HTML maintainer note that cannot render, and a body starting below the `h1` the page supplies.
The text is the published page carried over in full — controller and postal address, the two storage modes, CalDAV sync (what is stored, what is transmitted, RFC 6764 discovery, Nextcloud Login Flow v2, the user-CA trade-off), reminders and export, backups, crash reports, external links, permissions, distribution channels, deletion paths and GDPR rights.
Two things differ from the older short version that lived on `feat/caldav-sync`:
- Contact is `business@jeanlucmakiola.de`, matching the site and Calendula's policy, rather than `mail@`.
- Cleartext HTTP is described as refused outright. `CalDavDiscovery.allowCleartext` is `false` with nothing wiring it true, and `network_security_config.xml` sets `cleartextTrafficPermitted="false"`, so the previous "unless you explicitly opt in for a specific account" described a feature that does not exist. It goes back if a per-account opt-in ships.
Split out of `feat/caldav-sync` so the website change is not waiting on the whole sync branch. No issue to close — there is no open privacy/policy issue to reference.
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/12
Ships the first community translations: German (98%) and Brazilian
Portuguese (100%), both from Weblate, plus the translation infrastructure
and the Codeberg forge move that had accumulated under Unreleased.
A minor bump rather than a patch: two new user-facing languages is a
feature, the way 0.3.0 was cut for Reminders.
versionCode is the derived 400 (MAJOR*10000 + MINOR*100 + PATCH), and
scripts/sync_changelog_to_fastlane.sh generated changelogs/400.txt so the
official F-Droid listing shows this version's "What's New". At 908 chars
it exceeds the script's 500-char advisory and may be truncated in-client
— consistent with past feature releases (0.2.0 ran 1811, 0.3.0 692).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146mhMLZ5AqJSZkADDc92Jz
The Weblate merge (#8) landed values-de and values-pt-rBR, but
locales_config.xml still listed only `en`. That file is the single source
of truth for which languages the app ships: AppLanguage parses it for the
in-app picker (Settings -> App language) and android:localeConfig points
the system per-app language settings at it. So both translations were
compiled into the APK and reachable by nothing — you could not pick either
one, and only a device already set to de/pt-BR would ever have seen them.
Adds the two BCP-47 tags. Note the tags are `de` and `pt-BR` while the
resource folders are values-de and values-pt-rBR; the `r` is an Android
resource-qualifier artefact and must not appear here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146mhMLZ5AqJSZkADDc92Jz