Add Android support via Tauri

The Rust core was already portable (caller-provided db paths, rustls rather
than openssl) and the shell already had the mobile entry point, so this is
mostly toolchain, provisioning, and platform-gating.

Toolchain:
- scripts/android-setup.sh installs what Android Studio doesn't: SDK
  cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is
  pinned rather than "latest" because sdkmanager's newest is often a beta
  (30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers
  Android Studio's bundled JDK 21, since Gradle rejects newer JDKs.
- Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is
  hand-edited and Android permissions have no tauri.conf.json equivalent.
- vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server.

Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a
phone. Add import_reference_db to adopt a desktop-built file instead, validated
by db::inspect_reference_db first so picking the wrong file is a clean error
rather than a broken install. Installs via copy-then-rename so an interrupted
copy can't leave a partial db.

Ingest hardening (helps desktop too):
- Give ureq connect/read timeouts. Cancellation is only polled per parsed
  record, so a stalled socket previously hung forever with no way to cancel.
- Drop the remove_file before rename; POSIX rename already replaces atomically,
  and the delete-first window could leave no reference.db at all if the process
  was killed -- much likelier on Android.

UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path
equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile)
flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard
doesn't break the shell.

Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds
(bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a
device.
This commit is contained in:
2026-08-02 13:16:34 -05:00
parent bde9cf8a98
commit d3154738d2
52 changed files with 1411 additions and 21 deletions

View File

@@ -110,11 +110,53 @@ npm run build # typecheck + build frontend
cargo build # build everything incl. the desktop app
```
## Mobile (future)
## Android
The core and frontend are platform-agnostic; `cargo tauri icon` already emitted
iOS/Android icon sets. Porting is adding `tauri ios` / `tauri android` targets —
only reference-data provisioning differs (download on-device).
The Rust core and frontend are platform-agnostic, so the Android build shares
all app logic with the desktop; only data provisioning differs.
### One-time setup
Install Android Studio and **launch it once** so its wizard downloads the SDK,
then:
```bash
./scripts/android-setup.sh
```
That adds what Android Studio doesn't install by default: the SDK
command-line tools, a pinned stable **NDK**, and the Rust Android targets. It
prints the three environment variables to export (`ANDROID_HOME`, `NDK_HOME`,
`JAVA_HOME`).
> **Why the NDK?** `mtgc-core` builds `rusqlite` with the `bundled` feature
> (compiles SQLite from C) and `rustls` pulls in `ring` (C/asm), so a C
> cross-toolchain is required. The bundled SQLite can't be swapped for
> Android's system libsqlite3 either — the card schema needs FTS5 with the
> `trigram` tokenizer, which platform SQLite often lacks.
> **Why `JAVA_HOME`?** Gradle/AGP reject very new JDKs. The script prefers
> Android Studio's bundled JetBrains Runtime (JDK 21), which needs no extra
> install.
### Build and run
```bash
cargo tauri android init # once — scaffolds src-tauri/gen/android
cargo tauri android dev # run on an emulator or adb-connected device
cargo tauri android build --apk
```
`src-tauri/gen/android` is committed (unlike the rest of `gen/`) because
`AndroidManifest.xml` is hand-edited — Android permissions have no
`tauri.conf.json` equivalent.
### Card data on a phone
Downloading card data streams ~740MB of JSON to build a ~73MB database, which
is slow on mobile and may be killed if the app is backgrounded. Faster: copy
`reference.db` from a desktop install onto the device and use **Import card
data…**. The file is validated before it replaces anything.
## Branding