docs: how to run the Android client locally (task 1864)

Two things stop a fresh clone from opening in Android Studio, and both fail with
errors that name the wrong culprit — so they are written down rather than
rediscovered.

Android Studio runs Gradle on its bundled JDK 25, which Gradle 8.14.3 rejects
with an "Incompatible Gradle JVM version" message that reads like a project
misconfiguration. And settings.gradle applies tauri.settings.gradle, which is
generated per build and gitignored, so sync fails before anything can create it —
one CLI build fixes that permanently.

Also records why the Gradle pin is what it is, since the question came up and the
answer was not what it first looked like: the wrapper, the AGP pin and the
buildSrc file using the removed project.exec are all TRACKED in this repo. It is
scaffolding tauri android init wrote once, ours to bump when it is worth doing,
not a constraint of the framework. Tauri's own Android layer targets compileSdk
36 and registers back handling through OnBackPressedDispatcher — the library is
current, only the generated template trails.

Known gaps are listed so a tester does not file them as bugs: no safe-area
handling yet (2706), no enableOnBackInvokedCallback so predictive back will not
animate, and the templated app-wide usesCleartextTraffic that Minstrel already
hit as a Play Protect smell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 22:46:38 -04:00
co-authored by Claude Opus 5
parent 40cb463be7
commit c28f2bc00e
+117
View File
@@ -0,0 +1,117 @@
# Running the Android client locally
The Android app is the same Vue frontend and Rust core as the desktop, built by
Tauri v2 mobile. This sheet is the local setup; CI's copy of it is the
`ci-tauri-android` image (see `ci-requirements.md`).
**Open this directory in Android Studio:**
```
desktop/src-tauri/gen/android
```
Two things must be true first, and both fail confusingly if they aren't.
## 1. Gradle must run on JDK 17
Android Studio defaults to its own bundled JDK, which is currently 25. That
produces:
> Incompatible Gradle JVM version — The project's Gradle version 8.14.3 is
> incompatible with the Gradle JVM version 25 currently selected to run Gradle
> build.
Fix: `Settings → Build, Execution, Deployment → Build Tools → Gradle → Gradle
JDK`**17**. If none is listed, `Download JDK…` → version 17, vendor Eclipse
Temurin.
Prefer 17 explicitly over Android Studio's "apply a compatible JDK" quick-fix,
which will likely pick 21. 17 is what `ci-tauri-android` pins, and matching it
means a local build and a CI build resolve identically.
**Why 8.14.3 and not Gradle 9:** `cargo tauri android init` generated the wrapper
pin, and Gradle 9 removed `project.exec`, which the generated
`buildSrc/.../BuildTask.kt` still uses. The wrapper, the AGP pin and that file are
all tracked in this repo, so the bump is ours to make whenever it is worth doing —
it is scaffolding we inherited, not a requirement of Tauri. Tauri's own Android
layer targets `compileSdk 36` and registers back handling through
`OnBackPressedDispatcher`, so the library is current; only the template trails.
## 2. The Gradle glue has to be generated once
`settings.gradle` does `apply from: 'tauri.settings.gradle'`, and that file — with
`app/tauri.build.gradle.kts`, `app/tauri.properties`,
`app/src/main/assets/tauri.conf.json` and the compiled `.so` files — is generated
per build and gitignored. A fresh clone does not have them, and settings
evaluation happens before any Gradle task, so the sync fails before anything can
generate them.
From `desktop/src-tauri`:
```sh
cargo tauri android build --debug --target x86_64
```
After that, Android Studio syncs cleanly.
## Prerequisites
Gradle shells out to `cargo tauri android android-studio-script` on every build,
so the Rust toolchain is required on the machine running Android Studio — the CI
image cannot stand in for it.
```sh
rustup toolchain install 1.97.1
rustup target add aarch64-linux-android x86_64-linux-android
cargo install tauri-cli --version 2.11.4 --locked
```
Via the SDK Manager, matching `CI-tauri-android/versions.env`:
| | version | why it matters |
|---|---|---|
| Platform | API 36 | `compileSdk` / `targetSdk` |
| Build-Tools | 35.0.0 | AGP 8.11's default |
| **NDK** | 28.2.13676358 | the Rust core cannot cross-compile without it |
| Platform-Tools | latest | `adb` |
```sh
export ANDROID_HOME=$HOME/Android/Sdk
export NDK_HOME=$ANDROID_HOME/ndk/28.2.13676358
```
Node is needed too — `beforeBuildCommand` runs `npm ci && npm run build`, because
`generate_context!` compiles the frontend into the binary.
## Emulator
Create an AVD with an **x86_64** system image, API 24 or newer (`minSdk = 24`).
CI builds arm64-v8a and x86_64, so either runs, but x86_64 executes natively on a
desktop instead of being translated.
## The dev loop
Android Studio's Run button rebuilds the whole Rust core each time. For
iteration, with an emulator already running:
```sh
cd desktop/src-tauri
cargo tauri android dev
```
That starts Vite, wires `adb reverse` so the emulator reaches
`localhost:5173`, and hot-reloads frontend changes without a rebuild. Android
Studio earns its keep for the debugger, logcat and layout inspection.
## Known gaps, so they are not mistaken for bugs
- **Safe areas / viewport** are not handled yet (task 2706) — content may sit
under a notch or the gesture bar. Deliberately left until a device could
verify it.
- **`android:enableOnBackInvokedCallback`** is absent from the manifest, so
predictive-back animations will not engage on Android 13+. Tauri registers the
dispatcher correctly; only the opt-in flag is missing.
- **`android:usesCleartextTraffic`** is templated in app-wide. A
`networkSecurityConfig` resource is the better shape — see Scribe note 2437,
which hit this on Minstrel.
- Debug builds are large and slow to start. Do not read either as the product.