Removes AI-writing patterns across the docs, store listing and templates: dash-heavy rhythm, not-X-but-Y contrasts, one-line kickers and decorative bold. Facts, code, links and headings are unchanged. Also fixes leftovers copied from the sibling apps: the crash report template promised "no calendar content", and CONTRIBUTING described Agendula's task-provider layout, test seams and commit style.
127 lines
6.1 KiB
Markdown
127 lines
6.1 KiB
Markdown
# Contributing to Clockula
|
|
|
|
Thanks for your interest in Clockula, a Material 3 Expressive clock app for
|
|
alarms, timers, stopwatch and world clock. Unlike its siblings it owns its
|
|
storage, because there is no open provider behind a clock. Before you start, skim [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
|
|
(how it's built), [`docs/ROADMAP.md`](docs/ROADMAP.md) (what's next), and
|
|
[`docs/PLAN.md`](docs/PLAN.md) (the design rationale). This file covers the
|
|
practical how.
|
|
|
|
## The one architectural rule
|
|
|
|
Everything above the data layer talks to a repository and sees only domain types
|
|
and Flows. **Room entities, DAOs and `@Query` strings never leak above the data
|
|
layer.** Clockula owns its storage, which makes this rule more important here
|
|
than in the siblings, not less: it is the only thing keeping the schema a
|
|
private implementation detail rather than the app's de-facto domain model. If a
|
|
change would expose a Room type to a ViewModel or the UI, it's in the wrong
|
|
layer; see [`docs/PLAN.md`](docs/PLAN.md) §0.
|
|
|
|
## Prerequisites
|
|
|
|
- JDK 17
|
|
- Android SDK: compileSdk 37, build-tools 36.0.0 (the Gradle wrapper handles AGP/Kotlin)
|
|
- A real device for anything touching the alarm engine. An emulator will not
|
|
tell you whether an alarm survives doze, a reboot, or an OEM battery-saver,
|
|
and that is the only question that matters about an alarm.
|
|
|
|
## Build, test, lint
|
|
|
|
```sh
|
|
./gradlew :app:assembleDebug # build the debug APK
|
|
./gradlew :app:testDebugUnitTest # JVM unit tests (JUnit5 + Truth + Turbine)
|
|
./gradlew lintDebug # Android lint (CI runs this on every PR)
|
|
```
|
|
|
|
CI (`.forgejo/workflows/ci.yaml`, on Codeberg) runs a reproducible-release invariant check,
|
|
then lint → unit tests → debug build on every pull request, so run these locally
|
|
before opening a PR. Keep CI green.
|
|
|
|
## Translations
|
|
|
|
**Never edit a `values-*/strings.xml` file in a pull request.** 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 Clockula on Weblate](https://weblate.dev.jeanlucmakiola.de/engage/clockula/)**
|
|
|
|
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
|
|
|
|
```sh
|
|
python3 scripts/check_translations.py
|
|
```
|
|
|
|
before pushing. It reports those more clearly than lint's `MissingTranslation`
|
|
does, and it's what the `Translations` check runs on every PR.
|
|
|
|
A new language also needs one `<locale>` line in
|
|
`app/src/main/res/xml/locales_config.xml`. That file is the single source of
|
|
truth for both the in-app picker and the Android 13+ per-app language setting.
|
|
|
|
## Where to put code
|
|
|
|
| Layer | Lives in | Rule of thumb |
|
|
|---|---|---|
|
|
| Pure logic (models, scheduling and ring policies, formatting, the self-check rules) | `domain/` | No Android imports; must be JVM-unit-testable. |
|
|
| Storage | `data/` (`db/`, `alarms/`, `timers/`, `stopwatch/`, `worldclocks/`, `prefs/`, `backup/`) | The only place that knows Room or DataStore exists. Everything above it goes through a repository interface. |
|
|
| Engines and their Android seams | `alarm/`, `timer/`, `stopwatch/`, `ring/`, `interop/`, `widget/` | The engine is plain Kotlin; AlarmManager, services and receivers sit behind seams in its `android/`, `receiver/` and `service/` subpackages. |
|
|
| Screens | `ui/<area>/` | One ViewModel + immutable `UiState` per area; Compose for the screen. |
|
|
|
|
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) §3 lists every package.
|
|
|
|
## Code style & conventions
|
|
|
|
- **Kotlin**, 4-space indent, LF line endings, final newline, no trailing
|
|
whitespace, all enforced by `.editorconfig` (2-space for yaml/toml/json/md).
|
|
Match the surrounding code.
|
|
- **Material 3 Expressive** for all UI: use `MaterialExpressiveTheme`, the
|
|
colour-scheme tokens (never hardcoded colours), and canonical M3 components
|
|
(e.g. `ListItem` for rows). Consult the `material-3` skill before designing a
|
|
new screen or component.
|
|
- Prefer the domain layer for anything testable; keep Android APIs behind the
|
|
seams in each engine's `android/` subpackage so the rest stays JVM-testable.
|
|
|
|
## Tests
|
|
|
|
- New domain logic (scheduling, ring policies, formatting, mappers) **must**
|
|
come with JVM unit tests under `app/src/test/`. Repositories and engine seams
|
|
are the JVM-testable boundary: fake them rather than reaching for
|
|
instrumentation.
|
|
- Add an instrumented test only when a path genuinely needs a real device
|
|
(Room migrations, AlarmManager, a foreground service).
|
|
|
|
## Commits & PRs
|
|
|
|
- Write focused commits with clear messages (the existing history uses short,
|
|
conventional subjects like `feat(widgets): ...` / `fix(ringtones): ...`).
|
|
- Update [`CHANGELOG.md`](CHANGELOG.md) under `[Unreleased]` for any
|
|
user-visible change. Its sections feed the release notes and F-Droid "What's
|
|
New" (see [`docs/RELEASING.md`](docs/RELEASING.md)).
|
|
- If your change shifts the architecture or completes a milestone, update
|
|
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) / [`docs/ROADMAP.md`](docs/ROADMAP.md)
|
|
in the same PR.
|
|
- Don't bump `versionName` / `versionCode` in a regular PR. The committed
|
|
`versionName` is bumped only when **cutting a release** (that bump reaching
|
|
`main` is what triggers the release; the pipeline then mints the tag). See
|
|
[`docs/RELEASING.md`](docs/RELEASING.md).
|
|
|
|
## Scope
|
|
|
|
v1 is four surfaces (alarms, timers, stopwatch, world clock) done properly,
|
|
and nothing else. Widgets, a Quick Settings tile, screensaver mode and a bedtime
|
|
schedule are all wanted, and all deliberately deferred until v1 is finished; see
|
|
[`docs/ROADMAP.md`](docs/ROADMAP.md).
|
|
|
|
Two things are out of scope by design. **A sync stack of our own**: your alarms
|
|
export to a documented JSON file, and syncing them is a job for something that
|
|
already does syncing well. **Anything that makes an alarm less reliable in
|
|
exchange for a feature**: if a change trades away the certainty that an alarm
|
|
rings, it loses, however nice the feature is.
|
|
|
|
## License
|
|
|
|
By contributing you agree your contributions are licensed under the project's
|
|
[MIT License](LICENSE).
|