mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
Upstream mpv-android moved its build to NDK r30 (30.0.16248370), and each mpv-android revision pins its own NDK, so the two move together. The new revision also updates harfbuzz to 14.5.0, unibreak to 8.0, fribidi to 1.0.17, libxml2 to 2.15.4, fontconfig to 2.18.3 and curl to 8.22.0, and builds libass with assembly on x86_64 (nasm, already installed in CI). r30 brings an LLVM update with codegen and LTO fixes and sysroots up to API 37. It is also the NDK current SDK managers install, so a fresh setup no longer needs r29 installed beside it. The script change invalidates the cached libmpv tree, so CI rebuilds it once. The staged libraries are the same set as before, all 16 KiB aligned.
116 lines
5.7 KiB
Markdown
116 lines
5.7 KiB
Markdown
# Android Development
|
|
|
|
The Android client lives in `android/`; its Rust boundary is
|
|
`crates/pstr-android`. It targets Android 12 (API 31) and newer on
|
|
`arm64-v8a` phones/tablets and `x86_64` emulators. Compose owns navigation and
|
|
screens, while UniFFI exposes the existing catalog, share, streaming, watch-state,
|
|
and offline logic. Keep reusable behavior in the Rust crates rather than
|
|
reimplementing it in Kotlin.
|
|
|
|
## Toolchain
|
|
|
|
Install JDK 17, Android SDK Platform/Build Tools 36, NDK
|
|
`30.0.16248370`, Rust 1.96, Gradle 8.13, and the native helpers:
|
|
|
|
```bash
|
|
rustup target add aarch64-linux-android x86_64-linux-android
|
|
cargo install cargo-ndk --version 4.1.2 --locked
|
|
```
|
|
|
|
Set `ANDROID_HOME` (or `ANDROID_SDK_ROOT`); `cargo-ndk` discovers the NDK below
|
|
that SDK. The workspace-provided `uniffi-bindgen` binary keeps binding generation
|
|
on the same UniFFI version as the bridge. Use `bash scripts/build-android.sh debug` for an installable debug APK,
|
|
`bash scripts/build-android.sh check` for Android lint/unit tests, and
|
|
`bash scripts/build-android.sh release` for release APK/AAB output. Gradle first
|
|
generates UniFFI Kotlin into `android/app/build/generated/source/uniffi`, then
|
|
builds both Rust ABIs into `android/app/build/generated/jniLibs`. Generated files
|
|
belong under `build/` and must not be committed.
|
|
|
|
`versionName` always comes from `[workspace.package].version` in `Cargo.toml`, so
|
|
a tagged build cannot drift from the desktop release. The default `versionCode`
|
|
is `major * 1,000,000 + minor * 1,000 + patch` (`0.1.1` becomes `1001`). Set
|
|
`ANDROID_VERSION_CODE` only when Play requires a higher monotonic code; Gradle
|
|
rejects non-integers, zero, and values above Android's limit. The Android
|
|
workflow tests both the default derivation and the override path.
|
|
|
|
## Local Run and Verification
|
|
|
|
Start an API 31+ emulator or attach a device, then run:
|
|
|
|
```bash
|
|
bash scripts/build-android.sh debug
|
|
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
|
```
|
|
|
|
Before submitting Android work, run the Rust workspace gate from the repository
|
|
root, then:
|
|
|
|
```bash
|
|
bash scripts/build-android.sh check # ktlint, Android lint, host and screenshot tests
|
|
bash scripts/android-acceptance.sh --release # the on-device matrix, minified APK
|
|
```
|
|
|
|
`check` needs no device. Run the matrix with `--release` where you can: it
|
|
installs the signed minified APK and runs instrumentation against it, which is
|
|
the only path that exercises R8 — the step that has broken this app five distinct
|
|
ways that a debug build cannot reproduce. Without the `ANDROID_RELEASE_*`
|
|
variables below, drop `--release` and the debug APK is used instead. The matrix — phone and tablet layouts, rotation,
|
|
process recreation, playback and background controls, Picture-in-Picture,
|
|
download cancellation/resume, and an offline launch with networking disabled —
|
|
is `scripts/android-acceptance.sh`; `--list` prints every case and what it
|
|
needs, and `PSTR_ACCEPTANCE_ONLY=name` runs one. Five cases need a real share,
|
|
given as `PSTR_ACCEPTANCE_SHARE_URL` and `PSTR_ACCEPTANCE_SHARE_PASSWORD`; they
|
|
reach the device as instrumentation arguments and are stored nowhere. Without
|
|
them those cases report `skip`. `picture-in-picture` is still `pending`, so the
|
|
run tells you the matrix is incomplete — but no longer because of B40, which is
|
|
fixed; the case now just needs writing. `docs/TESTING.md` has the layer-by-layer
|
|
detail, including why lint runs against a baseline.
|
|
|
|
Requires JDK 17. `build-android.sh` locates it, because a newer JVM does not
|
|
fail with a version message — the Kotlin compiler throws
|
|
`IllegalArgumentException: 26.0.2` from an IntelliJ version parser.
|
|
|
|
## Release Signing
|
|
|
|
Release signing is optional locally and never stored in the repository. Set all
|
|
four variables before `bash scripts/build-android.sh signed`:
|
|
|
|
```text
|
|
ANDROID_RELEASE_STORE_FILE
|
|
ANDROID_RELEASE_STORE_PASSWORD
|
|
ANDROID_RELEASE_KEY_ALIAS
|
|
ANDROID_RELEASE_KEY_PASSWORD
|
|
```
|
|
|
|
GitHub Actions expects the keystore itself as the base64-encoded
|
|
`ANDROID_RELEASE_KEYSTORE_BASE64` secret and the other three values as secrets.
|
|
Keep the keystore and passwords in a durable external secret manager; losing the
|
|
key prevents users from upgrading an installed APK.
|
|
|
|
A `v*` tag runs `release.yml`, which calls `android.yml` with
|
|
`signed_release: true` and publishes the signed APK as
|
|
`proton-stream-<version>-android.apk` next to the desktop artifacts, together
|
|
with the `mpv-android-source-<rev>.tar.gz` corresponding source for the bundled
|
|
libmpv. The AAB is kept as the `android-bundle` workflow artifact only; nothing
|
|
uploads it to Play. A manual `android.yml` run with `signed_release` builds the
|
|
same signed files without publishing them.
|
|
|
|
## Licensing and Native Playback
|
|
|
|
The Android application is GPL-3.0-or-later; the shared Rust crates remain MIT.
|
|
See `android/LICENSE.md` and `android/THIRD_PARTY_NOTICES.md`. Before distributing
|
|
a binary with bundled libmpv, pin its exact source revision/build configuration,
|
|
package the notices, and retain the corresponding source plus build scripts
|
|
required by the GPL. The current playback service scaffold is not a substitute
|
|
for the final bundled libmpv integration.
|
|
|
|
`bash scripts/build-libmpv-android.sh` pins and stages libmpv for both supported
|
|
ABIs, plus a source archive and revision record, under
|
|
`android/app/build/generated/mpv`. Gradle runs that task before native builds,
|
|
packages the staged libraries, and compiles `pstr_mpv`, the JNI/EGL adapter. It
|
|
feeds libmpv through the Rust stream C ABI; decrypted bytes remain in native
|
|
memory. `PlaybackService` owns the mpv core for background audio and media
|
|
controls, while the activity supplies the current `Surface` and enters PiP.
|
|
Do not publish an Android binary until this exact pipeline is verified on real
|
|
arm64 hardware and an x86_64 emulator.
|