feat(android): console UI, wake-on-LAN wait-until-up, host edit + TV/tablet polish

Bring the Android client to parity with Apple's gamepad experience and finish
Wake-on-LAN.

- Console/gamepad home: host carousel, aurora chrome, mTLS game-library coverflow,
  and an input-aware legend that switches between gamepad face buttons and a
  TV-remote select-ring + arrows based on the last-used input.
- Wake-on-LAN: the fire-and-forget send is upgraded to wait-until-up
  (WakeController/WakeOverlay: resend + mDNS poll, 90s timeout, cancel/retry,
  fingerprint-matched so a host that cold-boots onto a new DHCP IP still connects),
  plus host edit (touch dialog + console form) with an auto-filled MAC.
- Android TV: brand banner (android:banner), density-aware console scaling, D-pad/
  remote nav (Up = Settings, Down or the pad Select button = host Options),
  emergency stream-exit chord, and 120Hz console refresh.
- Touch UI: settings split into subpages with a tablet NavigationRail, axis-aware
  tab animation (horizontal on phones, vertical on the tablet rail), animated
  settings navigation, and a licenses screen with a back button + the real
  workspace version (read from Cargo.toml).
- Vector Lock/controller icons (no emoji); bundled Geist font.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 20:04:47 +02:00
parent c2bc72a8e9
commit 4a87cef98c
42 changed files with 4247 additions and 361 deletions
+12 -5
View File
@@ -15,8 +15,10 @@ android {
ndkVersion = ndkVer
defaultConfig {
minSdk = 31
ndk { abiFilters += listOf("arm64-v8a", "x86_64") }
minSdk = 28 // Android 9 — reaches older TV boxes; API 31+ features are runtime-gated.
// Keep in lockstep with :app — 32-bit armeabi-v7a for the many 32-bit Google TV / Android TV
// boxes, 64-bit arm64-v8a for phones + modern TV, x86_64 for the emulator.
ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64") }
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
@@ -28,6 +30,9 @@ android {
kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_21) } }
dependencies {
// mTLS HTTPS client for the host's management API (the game-library fetch + cover-art loads).
// OkHttp lets us present the paired client cert and pin the host's self-signed cert by SHA-256.
implementation("com.squareup.okhttp3:okhttp:4.12.0")
testImplementation("junit:junit:4.13.2") // JVM unit test for the pure TXT parser
}
@@ -85,9 +90,11 @@ fun registerCargoNdk(taskName: String, release: Boolean) =
// find their subtools.
val cmd = mutableListOf(
"$cargoBin/cargo", "ndk",
"-t", "arm64-v8a", "-t", "x86_64",
// Link against the minSdk-31 sysroot so libaaudio (API 26+) is found.
"--platform", "31",
"-t", "arm64-v8a", "-t", "armeabi-v7a", "-t", "x86_64",
// Link against the minSdk-28 sysroot: libaaudio (API 26) is present, and building at the
// floor makes the linker reject any accidental >28 hard import (the one API-30 call we
// make, ANativeWindow_setFrameRate, is dlsym-resolved — see decode::try_set_frame_rate).
"--platform", "28",
"-o", file("src/main/jniLibs").absolutePath,
"build", "-p", "punktfunk-client-android",
)