feat: android-kotlin template (Compose + Material 3 Expressive, hermetic gradle builds, F-Droid recipe) #7

Open
jingus wants to merge 2 commits from t3code/android-kotlin-template into main
Owner

feat: android-kotlin template

A native Android starter: Kotlin + Jetpack Compose with the latest
Material 3 Expressive features, tests at every level, hermetic nix
builds, a release pipeline, and F-Droid tooling.

What's in it

  • App (templates/android-kotlin/app/) — a barebones "Launch
    checklist": MaterialExpressiveTheme with dynamic color (Android
    12+) and the expressive motion scheme, LinearWavyProgressIndicator,
    LoadingIndicator, cookie-shaped (MaterialShapes.Cookie9Sided)
    extended FAB, press-morphing list rows, segmented-button filter,
    bottom sheet. No auth, no network, no permissions.
  • Stack — AGP 9.4.1 (built-in Kotlin), Gradle 9.8.1 (wrapper
    pinned + sha256-verified), Kotlin 2.4.21, Compose BOM 2026.09.00,
    material3 1.5.0-beta01 (expressive APIs), committed gradle lockfiles
    (strict locking).
  • Tests — pure-JVM ViewModel tests (run hermetically in
    nix flake check), Compose UI tests on the JVM via Robolectric
    native graphics (no emulator), a screenshot test that renders the
    real UI to PNG, and an instrumented test for devices.
  • Nix (gradle.nix + nix-modules/) — a fixed-output dependency
    cache rendered as a byte-deterministic maven mirror (gradle's
    own metadata bins can't be pinned — per-JVM hash ordering — so the
    offline builds re-derive them from the mirror; verified
    reproducible), fully offline apk/apk-release derivations,
    ktlint/unit-test/apk-smoke checks, devshell (JDK 17 + androidenv
    SDK, license acceptance scoped to the flake), validate and
    update-deps-hash apps.
  • CI / release — the template ships ci.yml (canonical gates for
    instantiated projects) and release.yml (tag v* → nix-built
    release APK attached to a forgejo release).
  • F-Droid — fdroid/dev.starter.android.yml build recipe plus
    fastlane/metadata/ store listing.
  • Root CI registers the template but skips the per-push
    instantiate-and-validate job for it (multi-GB gradle caches);
    validate locally after touching it, as documented.

How to test

# 1. root gates (fast)
nix flake check

# 2. instantiate exactly like a user (in an empty dir)
mkdir /tmp/android-starter && cd /tmp/android-starter
nix flake init -t path:/path/to/this/repo#android-kotlin
nix flake check          # ktlint + hermetic unit tests + both APKs + smoke
nix run .#validate       # wrapper/lock freshness + full test suite
                          # (incl. Robolectric UI tests; needs network)

# 3. hold the APK
nix build .#apk-release  # result/app-release.apk (debug-signed)

# 4. screenshot the UI without an emulator
mkdir -p /tmp/shots
sh ./gradlew :app:testDebugUnitTest --tests "*ScreenshotTest" \
  -Dstarter.screenshot.dir=/tmp/shots
xdg-open /tmp/shots/starter-home.png

# 5. dev loop
nix develop              # jdk 17 + android sdk + ktlint
sh ./gradlew :app:installDebug    # or assembleDebug

All of the above was verified on a fresh init copy before opening
this PR (steps 1–4 green; the README screenshot was produced by step
4's test).

# feat: android-kotlin template A native Android starter: Kotlin + Jetpack Compose with the latest **Material 3 Expressive** features, tests at every level, hermetic nix builds, a release pipeline, and F-Droid tooling. ## What's in it - **App** (`templates/android-kotlin/app/`) — a barebones "Launch checklist": `MaterialExpressiveTheme` with dynamic color (Android 12+) and the expressive motion scheme, `LinearWavyProgressIndicator`, `LoadingIndicator`, cookie-shaped (`MaterialShapes.Cookie9Sided`) extended FAB, press-morphing list rows, segmented-button filter, bottom sheet. No auth, no network, no permissions. - **Stack** — AGP 9.4.1 (built-in Kotlin), Gradle 9.8.1 (wrapper pinned + sha256-verified), Kotlin 2.4.21, Compose BOM 2026.09.00, material3 1.5.0-beta01 (expressive APIs), committed gradle lockfiles (strict locking). - **Tests** — pure-JVM ViewModel tests (run hermetically in `nix flake check`), Compose UI tests on the JVM via Robolectric native graphics (no emulator), a screenshot test that renders the real UI to PNG, and an instrumented test for devices. - **Nix** (`gradle.nix` + `nix-modules/`) — a fixed-output dependency cache rendered as a byte-deterministic **maven mirror** (gradle's own metadata bins can't be pinned — per-JVM hash ordering — so the offline builds re-derive them from the mirror; verified reproducible), fully offline `apk`/`apk-release` derivations, ktlint/unit-test/apk-smoke checks, devshell (JDK 17 + androidenv SDK, license acceptance scoped to the flake), `validate` and `update-deps-hash` apps. - **CI / release** — the template ships `ci.yml` (canonical gates for instantiated projects) and `release.yml` (tag `v*` → nix-built release APK attached to a forgejo release). - **F-Droid** — `fdroid/dev.starter.android.yml` build recipe plus `fastlane/metadata/` store listing. - Root CI registers the template but **skips** the per-push instantiate-and-validate job for it (multi-GB gradle caches); validate locally after touching it, as documented. ## How to test ```sh # 1. root gates (fast) nix flake check # 2. instantiate exactly like a user (in an empty dir) mkdir /tmp/android-starter && cd /tmp/android-starter nix flake init -t path:/path/to/this/repo#android-kotlin nix flake check # ktlint + hermetic unit tests + both APKs + smoke nix run .#validate # wrapper/lock freshness + full test suite # (incl. Robolectric UI tests; needs network) # 3. hold the APK nix build .#apk-release # result/app-release.apk (debug-signed) # 4. screenshot the UI without an emulator mkdir -p /tmp/shots sh ./gradlew :app:testDebugUnitTest --tests "*ScreenshotTest" \ -Dstarter.screenshot.dir=/tmp/shots xdg-open /tmp/shots/starter-home.png # 5. dev loop nix develop # jdk 17 + android sdk + ktlint sh ./gradlew :app:installDebug # or assembleDebug ``` All of the above was verified on a fresh init copy before opening this PR (steps 1–4 green; the README screenshot was produced by step 4's test).
feat: android-kotlin template (Compose + Material 3 Expressive, hermetic gradle builds, F-Droid recipe)
Some checks failed
ci / flake-check (push) Successful in 19s
ci / flake-check (pull_request) Successful in 14s
ci / templates (push) Failing after 1m25s
ci / templates (pull_request) Failing after 56s
37a5ed1a95
merge: origin/main (rust-cli template) into android-kotlin-template
Some checks failed
ci / flake-check (push) Successful in 23s
ci / flake-check (pull_request) Successful in 16s
ci / templates (push) Failing after 1m3s
ci / templates (pull_request) Failing after 56s
01900fa622
Some checks failed
ci / flake-check (push) Successful in 23s
ci / flake-check (pull_request) Successful in 16s
ci / templates (push) Failing after 1m3s
ci / templates (pull_request) Failing after 56s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin 't3code/android-kotlin-template:t3code/android-kotlin-template'
git switch 't3code/android-kotlin-template'

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch 'main'
git merge --no-ff 't3code/android-kotlin-template'
git switch 't3code/android-kotlin-template'
git rebase 'main'
git switch 'main'
git merge --ff-only 't3code/android-kotlin-template'
git switch 't3code/android-kotlin-template'
git rebase 'main'
git switch 'main'
git merge --no-ff 't3code/android-kotlin-template'
git switch 'main'
git merge --squash 't3code/android-kotlin-template'
git switch 'main'
git merge --ff-only 't3code/android-kotlin-template'
git switch 'main'
git merge 't3code/android-kotlin-template'
git push origin 'main'
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jingus/starters!7
No description provided.