diff --git a/.forgejo/ISSUE_TEMPLATE/bug_report.md b/.forgejo/ISSUE_TEMPLATE/bug_report.md index 6a4e08a..16bac83 100644 --- a/.forgejo/ISSUE_TEMPLATE/bug_report.md +++ b/.forgejo/ISSUE_TEMPLATE/bug_report.md @@ -21,9 +21,9 @@ labels: - Calendula version: - Android version: - Device: -- Installed from: +- Installed from: - Affected calendar: - Time zone: diff --git a/.forgejo/ISSUE_TEMPLATE/config.yml b/.forgejo/ISSUE_TEMPLATE/config.yml index 2fc3df8..d0159e0 100644 --- a/.forgejo/ISSUE_TEMPLATE/config.yml +++ b/.forgejo/ISSUE_TEMPLATE/config.yml @@ -6,8 +6,8 @@ contact_links: - name: Translate Calendula url: https://weblate.dev.jeanlucmakiola.de/engage/calendula/ about: >- - Translations are managed on Weblate, not here — it owns every values-* - file, so a hand-edited translation gets overwritten on the next sync. + Translations are managed on Weblate. It owns every values-* file, so a + hand-edited translation gets overwritten on the next sync. No coding needed: pick or request a language and translate in the browser. - name: Contributing guide diff --git a/.forgejo/ISSUE_TEMPLATE/crash_report.md b/.forgejo/ISSUE_TEMPLATE/crash_report.md index 4626077..4cd9dee 100644 --- a/.forgejo/ISSUE_TEMPLATE/crash_report.md +++ b/.forgejo/ISSUE_TEMPLATE/crash_report.md @@ -1,6 +1,6 @@ --- name: Crash report -about: Report a crash. Calendula can capture this for you (Settings → Report a problem, or the prompt after a crash) — it copies the report to your clipboard and prefills this form. +about: Report a crash. Calendula can capture this for you (Settings → Report a problem, or the prompt after a crash). It copies the report to your clipboard and prefills this form. title: "Crash: " labels: - bug @@ -11,10 +11,10 @@ labels: ### What happened diff --git a/.forgejo/ISSUE_TEMPLATE/feature_request.md b/.forgejo/ISSUE_TEMPLATE/feature_request.md index 342118d..ef33df4 100644 --- a/.forgejo/ISSUE_TEMPLATE/feature_request.md +++ b/.forgejo/ISSUE_TEMPLATE/feature_request.md @@ -9,7 +9,7 @@ labels: ### What would you like Calendula to do? -### Why — what problem does it solve? +### What problem does it solve? ### Anything else diff --git a/.forgejo/PULL_REQUEST_TEMPLATE.md b/.forgejo/PULL_REQUEST_TEMPLATE.md index 48026ec..ab2714c 100644 --- a/.forgejo/PULL_REQUEST_TEMPLATE.md +++ b/.forgejo/PULL_REQUEST_TEMPLATE.md @@ -5,7 +5,7 @@ Please skim CONTRIBUTING.md if you haven't: https://codeberg.org/jlmakiola/calendula/src/branch/main/CONTRIBUTING.md Two things it's easy to get wrong: - • Features need a discussed issue first — an undiscussed feature PR may be + • Features need a discussed issue first. An undiscussed feature PR may be closed unmerged even when the code is good. • Target the release branch for your issue's milestone (milestone 2.18.0 → release/v2.18.0), not main. If you targeted main, just say so below and it @@ -17,7 +17,7 @@ Two things it's easy to get wrong: ### Why - + ### How it was tested @@ -34,7 +34,7 @@ are especially useful for UI changes. ### Checklist - [ ] There's an issue for this, and (for a feature) it got a go-ahead -- [ ] Targeting the release branch for that issue's milestone — or `main`, noted above +- [ ] Targeting the release branch for that issue's milestone (or `main`, noted above) - [ ] `./gradlew lint test assembleDebug` passes locally - [ ] No `values-*/strings.xml` touched (Weblate owns those; new English strings in `values/` are fine) - [ ] `CHANGELOG.md` updated under `## [Unreleased]`, if the change is user-visible diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5948114..651401b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,13 @@ # Contributing to Calendula Calendula is a Material 3 Expressive calendar app that lives strictly on top of -Android's `CalendarContract` — no app database, no sync stack, no network access. -That constraint shapes most review comments, so -[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) is worth skimming before you write -code. This file is the practical how. +Android's `CalendarContract`, with no app database, sync stack or network access. +That constraint shapes most review comments, so skim +[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) before you write code. This file +covers the practical side. -**[Codeberg](https://codeberg.org/jlmakiola/calendula) is the canonical home** — -issues, pull requests, releases. The self-hosted Gitea instance referenced in the +[Codeberg](https://codeberg.org/jlmakiola/calendula) is the canonical home for +issues, pull requests and releases. The self-hosted Gitea instance referenced in the release docs is build infrastructure only; there is nothing to contribute there. Be decent to the people you meet in the tracker. @@ -18,20 +18,20 @@ Be decent to the people you meet in the tracker. | Add a feature | **Open an issue first** and wait for a go-ahead | | Fix a bug | Open an issue, then a pull request | | Fix a typo, a comment, or docs | Just open the pull request | -| Add or fix a translation | **Don't** — [use Weblate](#translations) | +| Add or fix a translation | Use [Weblate](#translations), not a pull request | Features get an opinion before they get code: whether Calendula should do a thing at all is the one decision a patch can't make. A feature PR that arrives -without a discussed issue may be closed unmerged even when the code is good — +without a discussed issue may be closed unmerged even when the code is good, so please don't spend a weekend on one first. Bugs are more straightforward, but still start with an issue: it's what carries the milestone and gives the changelog something to link. Issue templates cover bug, crash, feature and question. For a crash, let the app -do the work — **Settings → Report a problem**, or the prompt shown after a crash, +do the work: *Settings → Report a problem*, or the prompt shown after a crash, captures the stack trace and prefills the form. The report contains app, Android -and device versions plus the trace; no calendar content, no personal data. +and device versions plus the trace, and no calendar content or personal data. ## Which branch to target @@ -45,20 +45,20 @@ Once your issue has a milestone, that milestone names your branch: | `2.18.0` | `release/v2.18.0` | Every milestone has a matching branch. If it's somehow missing, target `main` and -mention it in the PR — it will be retargeted. Don't pick an older release branch: +mention it in the PR, and it will be retargeted. Don't pick an older release branch: they're kept after shipping, so the newest one isn't necessarily yours. ## Translations -**Never edit a `values-*/strings.xml` file in a pull request** — German included. +**Never edit a `values-*/strings.xml` file in a pull request**, German included. Translations are owned by a self-hosted Weblate that writes to this repository directly, and a hand-edit is overwritten on the next sync. -→ **[Translate Calendula on Weblate](https://weblate.dev.jeanlucmakiola.de/engage/calendula/)** +[Translate Calendula on Weblate](https://weblate.dev.jeanlucmakiola.de/engage/calendula/) Adding a *new* English string to `values/strings.xml` is normal PR work; Weblate picks it up and offers it to translators. Partial translations are expected and -fine — missing keys are informational. Stale and orphaned keys are not, so run +fine; missing keys are informational. Stale and orphaned keys are not, so run ```sh python3 scripts/check_translations.py @@ -67,7 +67,7 @@ python3 scripts/check_translations.py before pushing. It reports those more clearly than lint's `MissingTranslation` does. -## Build & test +## Build and test ```sh git clone --recurse-submodules https://codeberg.org/jlmakiola/calendula.git @@ -77,11 +77,11 @@ The `floret-kit` submodule is a composite build compiled from source. An existing clone needs `git submodule update --init --recursive`, or nothing resolves. -- **JDK 17** — not newer; the Android Gradle Plugin requires exactly 17. Set +- **JDK 17**, not newer: the Android Gradle Plugin requires exactly 17. Set `JAVA_HOME` if your default differs. -- **Android SDK** — platform 37 (`compileSdk`) and build-tools 36.0.0, located +- **Android SDK**: platform 37 (`compileSdk`) and build-tools 36.0.0, located via `ANDROID_HOME` or a gitignored `local.properties` with `sdk.dir`. If you - go the `local.properties` route the included build needs its own copy at + go the `local.properties` route, the included build needs its own copy at `floret-kit/local.properties`; `ANDROID_HOME` covers both at once and is the easier path. @@ -109,21 +109,21 @@ These are the ones that turn into review comments. 1. **No network.** Calendula holds no `INTERNET` permission, and that's a feature rather than an oversight. Anything that would need one is a product - decision before it's a patch — the crash reporter deliberately opens a - prefilled web issue instead of posting anything itself. + decision before it's a patch. The crash reporter opens a prefilled web issue + on purpose instead of posting anything itself. 2. **The provider is the only database.** No Room, no cache, no local mirror of events. `CalendarContract` is the single source of truth, which is also why externally synced changes work for free. 3. **Don't patch UI state after a write.** A `ContentObserver` re-queries and views recompose from fresh provider state. Hand-patching a list after saving - appears to work, then quietly diverges from what the provider actually stored. + appears to work, then drifts away from what the provider stored. 4. **`domain/` has no Android imports.** Models, validation, recurrence rendering, conflict snapshots and the `.ics` codec stay pure Kotlin so they remain JVM-testable. 5. **Tests run on the JVM.** JUnit 5 + Truth + Turbine. The seams exist for you: fake the data source (`FakeCalendarDataSource`), and feed mappers plain maps through `ColumnReader` instead of cursors. Instrumented tests are a last - resort, not a default. + resort. 6. **Read before touching the subtle pipelines.** Recurring writes (UNTIL vs DURATION, exception URIs, series splits), save-conflict detection and reminder delivery (post-before-mark) follow provider-driven rules that are documented @@ -138,14 +138,14 @@ These are the ones that turn into review comments. ## UI conventions Material 3 Expressive throughout, built from the system's own tokens and -components — colour-scheme tokens rather than hardcoded colours, `ListItem` for +components: colour-scheme tokens rather than hardcoded colours, `ListItem` for settings rows. **Selection pickers are full-screen.** Every browse-style "choose one" surface uses floret-kit's `FullScreenPicker` / `OptionPicker`; one that needs a commit or extra action passes it through the picker's `actions` slot. The exception is the recurring-scope chooser (*this / this and following / all*), which stays a -compact dialog — a two- or three-option decision reads better as a popup than as +compact dialog, because a two- or three-option decision reads better as a popup than as a nearly empty screen. `AlertDialog` is for plain confirmations only, and radio- or text-list dialogs aren't used at all. @@ -154,7 +154,7 @@ Shared UI machinery lives in the `floret-kit` submodule and has changing it means a pull request against that repository plus a submodule bump here. -## Commits & pull requests +## Commits and pull requests Conventional commits, scoped to the area you touched: @@ -165,13 +165,13 @@ docs(architecture): record what the second review pass changed ``` Types in use: `feat` `fix` `docs` `refactor` `style` `chore` `ci` `build` -`revert`. Reference the issue in the subject or the body. Keep commits small — -small commits revert cleanly, which matters more here than a tidy history. +`revert`. Reference the issue in the subject or the body. Keep commits small. +Small commits revert cleanly, which matters more here than a tidy history. If your change is user-visible, add an entry under `## [Unreleased]` in [`CHANGELOG.md`](CHANGELOG.md). Match the surrounding voice: entries describe -what changed *for the person using the app*, and why, not what changed in the -code. Link the issue and add its reference at the bottom of the file. It may get +what changed *for the person using the app*, and why. Code changes don't go +there. Link the issue and add its reference at the bottom of the file. It may get reworded when the release is cut, so don't agonise over it. Please don't commit planning or design documents. Code, tests, architecture notes diff --git a/design/store/raw/03-agenda.png b/design/store/raw/03-agenda.png new file mode 100644 index 0000000..bd3048b Binary files /dev/null and b/design/store/raw/03-agenda.png differ diff --git a/design/store/raw/04-detail.png b/design/store/raw/04-detail.png new file mode 100644 index 0000000..c64ac11 Binary files /dev/null and b/design/store/raw/04-detail.png differ diff --git a/design/store/raw/05-recurring.png b/design/store/raw/05-recurring.png new file mode 100644 index 0000000..df9c4d4 Binary files /dev/null and b/design/store/raw/05-recurring.png differ diff --git a/design/store/raw/06-calendars.png b/design/store/raw/06-calendars.png new file mode 100644 index 0000000..a05393a Binary files /dev/null and b/design/store/raw/06-calendars.png differ diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 9577701..d4086ea 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -1,7 +1,7 @@ # Building from source -Calendula builds with the standard Android Gradle toolchain — no extra setup -beyond the SDK, a JDK, and the submodule. +Calendula builds with the standard Android Gradle toolchain. You need the SDK, +a JDK and the submodule, nothing else. ## Clone @@ -9,9 +9,9 @@ beyond the SDK, a JDK, and the submodule. git clone --recurse-submodules https://codeberg.org/jlmakiola/calendula.git ``` -Calendula depends on **[floret-kit](https://codeberg.org/jlmakiola/floret-kit)**, +Calendula depends on [floret-kit](https://codeberg.org/jlmakiola/floret-kit), the shared Material 3 Expressive kit, as a git submodule wired in as a Gradle -composite build (`includeBuild("floret-kit")` in `settings.gradle.kts`) — it is +composite build (`includeBuild("floret-kit")` in `settings.gradle.kts`). It is compiled from source, not resolved from a repository. A clone without the submodule fails to configure. For an existing clone: @@ -21,14 +21,14 @@ git submodule update --init --recursive ## Requirements -- **JDK 17** — not newer; the Android Gradle Plugin requires exactly 17. If your +- **JDK 17**, not newer: the Android Gradle Plugin requires exactly 17. If your default JDK differs, set `JAVA_HOME` explicitly. -- **Android SDK** — platform **37** (`compileSdk`) and **build-tools 36.0.0**. +- **Android SDK**: platform 37 (`compileSdk`) and build-tools 36.0.0. `minSdk` is 29, `targetSdk` 36. The SDK is located via `ANDROID_HOME` (or `ANDROID_SDK_ROOT`), or via a gitignored `local.properties` with `sdk.dir`. If you use `local.properties`, note -that the composite build needs **its own** copy at `floret-kit/local.properties`; +that the composite build needs its own copy at `floret-kit/local.properties`; setting `ANDROID_HOME` covers both builds at once and is the simpler route. The Gradle wrapper is checked in, so you don't need a system Gradle. diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index 3223d9a..adfd8a3 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -26,7 +26,7 @@ Applies to the Android app **Calendula** (package `de.jeanlucmakiola.calendula`) ## In short -Calendula collects nothing, sends nothing, and has no user accounts. It has **no internet permission at all** — the app is technically incapable of transmitting your data anywhere. Everything it shows you is read from the calendars that already exist on your device. +Calendula collects nothing, sends nothing, and has no user accounts. It has **no internet permission at all**, so it cannot transmit your data anywhere. Everything it shows you is read from the calendars that already exist on your device. ## 1. Controller @@ -47,9 +47,9 @@ All of the following is processed **locally on your device only**. None of it is ### Calendar data -Calendula is a viewer and editor for the calendars Android already manages. It reads and writes events, reminders and calendar settings through Android's system calendar provider. The app keeps **no database of its own** — your events live in the system calendar, exactly where they lived before you installed Calendula, and they remain there if you uninstall it. +Calendula is a viewer and editor for the calendars Android already manages. It reads and writes events, reminders and calendar settings through Android's system calendar provider. The app keeps **no database of its own**. Your events live in the system calendar, where they were before you installed Calendula, and they stay there if you uninstall it. -Note: if one of those system calendars is itself synchronised with an online account (for example a Google account, or a CalDAV server via DAVx5), that synchronisation is performed by Android and that other app — not by Calendula. The privacy policy of the respective provider applies to it. +Note: if one of those system calendars is itself synchronised with an online account (for example a Google account, or a CalDAV server via DAVx5), that synchronisation is performed by Android and that other app, not by Calendula. The privacy policy of the respective provider applies to it. ### Contacts (optional) @@ -67,7 +67,7 @@ When you import or export an ICS file, Calendula reads or writes exactly the fil Your preferences (view options, theme, reminder defaults and similar) are stored locally on your device and are removed when you uninstall the app. -## 4. Crash reports — the only case where data can leave your device +## 4. Crash reports: the only case where data can leave your device If Calendula crashes, it offers to report the problem. Nothing is sent automatically. The report is copied to your clipboard and your browser is opened with the project's issue tracker, the text pre-filled. **You see the full content, you decide whether to submit it, and you can edit or discard it.** @@ -86,19 +86,19 @@ If you choose to submit it, the report becomes a public issue on the project's i ## 5. External links -The settings screen contains links to the source code, the licence, the issue tracker and a voluntary donation page (Ko-fi). Following one of these links opens your browser and leaves the app; the privacy policy of the respective website then applies. Calendula transmits no data of yours in the process — it only opens the address. +The settings screen contains links to the source code, the licence, the issue tracker and a voluntary donation page (Ko-fi). Following one of these links opens your browser and leaves the app; the privacy policy of the respective website then applies. Calendula transmits none of your data in the process; it only opens the address. ## 6. Permissions and why they exist -- `READ_CALENDAR`, `WRITE_CALENDAR` — display and edit your events; the core function. -- `POST_NOTIFICATIONS` — show reminders. -- `READ_CONTACTS` — optional, only for the “Contact special dates” feature. -- `USE_EXACT_ALARM`, `SCHEDULE_EXACT_ALARM` — deliver reminders at the exact time, including after snoozing. -- `RECEIVE_BOOT_COMPLETED` — re-register pending reminders after a restart. -- `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` — only to open the system dialog for the “Reliable delivery” setting. -- `WAKE_LOCK`, `FOREGROUND_SERVICE`, `ACCESS_NETWORK_STATE` — required by the Android system component used for scheduled background work (WorkManager). +- `READ_CALENDAR`, `WRITE_CALENDAR`: display and edit your events (the core function). +- `POST_NOTIFICATIONS`: show reminders. +- `READ_CONTACTS`: optional, only for the “Contact special dates” feature. +- `USE_EXACT_ALARM`, `SCHEDULE_EXACT_ALARM`: deliver reminders at the exact time, including after snoozing. +- `RECEIVE_BOOT_COMPLETED`: re-register pending reminders after a restart. +- `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`: only to open the system dialog for the “Reliable delivery” setting. +- `WAKE_LOCK`, `FOREGROUND_SERVICE`, `ACCESS_NETWORK_STATE`: required by the Android system component used for scheduled background work (WorkManager). -`ACCESS_NETWORK_STATE` allows reading *whether* a network connection exists — it does **not** permit using one. Without `INTERNET`, no connection is possible. +`ACCESS_NETWORK_STATE` allows reading *whether* a network connection exists. It does **not** permit using one. Without `INTERNET`, no connection is possible. ## 7. Distribution channels diff --git a/docs/README.md b/docs/README.md index acb9257..7b4f047 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,17 +9,14 @@ Where to look for what: | [`ARCHITECTURE.md`](ARCHITECTURE.md) | Orientation tour: principles, layers, navigation, recurring-write / conflict / reminder pipelines, testing | | [`RELEASING.md`](RELEASING.md) | Release process: versioning, the merge-driven pipeline, the two-forge split, secrets, key custody | | [`../CHANGELOG.md`](../CHANGELOG.md) | Release history (Keep a Changelog, SemVer) | -| [Issues](https://codeberg.org/jlmakiola/calendula/issues) + [milestones](https://codeberg.org/jlmakiola/calendula/milestones) | **The roadmap.** What's planned, in progress, and shipped — a milestone maps to its `release/vX.Y.Z` branch | +| [Issues](https://codeberg.org/jlmakiola/calendula/issues) + [milestones](https://codeberg.org/jlmakiola/calendula/milestones) | The roadmap: what's planned, in progress and shipped. A milestone maps to its `release/vX.Y.Z` branch | | [`../.planning/PROJECT.md`](../.planning/PROJECT.md) | What the project is: core value, stack + version pins, constraints, naming, forge/release infrastructure | | [`design/`](design/) | Per-feature design notes kept for features whose provider behaviour is worth recording | | [`../fastlane/metadata/android/`](../fastlane/metadata/android/) | Store metadata (single source of truth) for every app language, mapped in `store-locales.txt`: descriptions, title, graphics. Harvested directly by the official F-Droid repo, pushed to Google Play with every release, validated by [`../scripts/check_store_listing.py`](../scripts/check_store_listing.py); transformed into the self-hosted repo layout at release time by [`../scripts/fastlane_to_fdroid_localized.sh`](../scripts/fastlane_to_fdroid_localized.sh) | | [`../fdroid-metadata/`](../fdroid-metadata/) | App-level F-Droid control file (`*.yml`: Categories, License, links) for the self-hosted repo's `fdroid update` | -| [`fdroid-official/`](fdroid-official/) | Recipe + notes for publishing to the **official** F-Droid repo (reproducible build + developer-signed binary) | +| [`fdroid-official/`](fdroid-official/) | Recipe and notes for publishing to the official F-Droid repo (reproducible build + developer-signed binary) | -Conventions: planning lives in the **issue tracker**, not in this repository. The -`.planning/` files that predated it (a roadmap, a development-state snapshot, and -a per-milestone requirement checklist) are gone — issues and milestones say the -same thing without going stale. `PROJECT.md` is what remains, and it describes -the project rather than its plan. -`ARCHITECTURE.md` is the authoritative orientation tour: it is updated with the -code, and is the right place for a lesson learned about the calendar provider. +Planning lives in the issue tracker and its milestones, not in this repository. +`.planning/PROJECT.md` describes the project itself. +`ARCHITECTURE.md` is updated with the code and is the place to record a lesson +learned about the calendar provider.