The Android client adopts the Skia console — one gamepad UI on three platforms, the Compose mirror deleted #301
Merged
enricobuehler
merged 5 commits from 2026-08-18 15:01:53 +00:00
worktree-android-skia-console into main
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba227057b6 |
build(android): the Skia archives are served from our own Gitea release, by default
ci / bun-nix (pull_request) Successful in 26s
ci / web (pull_request) Successful in 1m23s
ci / docs-site (pull_request) Successful in 1m25s
apple / swift (pull_request) Successful in 2m11s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 5m41s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 7m18s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 3m18s
android / android (pull_request) Successful in 12m3s
nix / flake (pull_request) Failing after 14m54s
ci / rust (pull_request) Successful in 17m49s
The WP6 hosting step lands where the project's git assets live: a public release on
git.unom.io (R2-backed) rather than the sccache bucket — unom/skia-binaries, tag 0.99.0,
mirroring rust-skia's own release layout so skia-bindings' {tag}/{key} url template fits
it verbatim:
https://git.unom.io/unom/skia-binaries/releases/download/{tag}/skia-binaries-{key}.tar.gz
The release carries the armv7-linux-androideabi archive we build (rust-skia publishes
none) and byte-for-byte mirrors of the aarch64/x86_64 assets, each with its sha256 and
provenance in the release notes.
kit/build.gradle.kts now bakes that template in as the DEFAULT for every cargo-ndk run —
dev boxes and CI need no configuration, GitHub is out of the Android build path entirely,
and `-PskiaBinariesUrl` / the SKIA_BINARIES_URL env (and CI's repo variable) remain as
overrides for staging the next skia-safe bump's archives before they are released.
Proven: unauthenticated downloads of all three archives byte-match the local sha256s;
a clean-target cargo-ndk armv7 check installs from the release (`DOWNLOAD AND INSTALL
SUCCEEDED`); assembleDebug with the skia-bindings build dirs wiped re-fetched all three
ABIs from the default and built green.
|
||
|
|
428fef2b20 |
refactor(android)!: the Compose console is deleted — the Skia shell is the console
WP7 of design/android-skia-console-port.md, unlocked by WP6 (every ABI carries the native host) and the on-glass pass. The third gamepad-UI implementation is gone: Android's console is now the same pf-console-ui shell Windows and Linux show, and the Compose mirror that re-implemented it screen by screen is deleted rather than parked. Deleted (~6.5 kLOC): GamepadHome, GamepadSettingsScreen, GamepadAddHostScreen, GamepadDialogs, HomeTiles, the console halves of LibraryScreen (coverflow, ConsoleLibrary), ConnectOverlay (ConnectTakeover), ConnectScreen / ConnectPrompts / AdaptiveDialogs (gamepadUi branches, console options dialog), App.kt's GamepadShell + GamepadScreen — and their tests (HomeTilesTest, GamepadSettingsLayout/RowsTest, ConsoleSubScreenRoutes/RowsTest, the buildSettingsRows pins in GamepadPaletteTest, the Compose-console shot scenes; the tab vocabulary is pinned by the Rust twin against the same shared vectors file). Kept, deliberately: GamepadChrome/Nav/Aurora/Ink/Palette — the Compose platform screens the console still opens (Controllers, Licences per D7), MainActivity's touch-UI pad navigation, and the settings palette preview all draw with them; LibraryPosition + the TOUCH library (the touch shell's own pushed screen); GamepadUi.kt (the mode switch). App.kt folds the native host's presence into `gamepadUi` itself: no host, no console — a controller then drives the touch UI through Compose focus (nothing left to fall back to). The sysprop becomes a triage switch (`debug.punktfunk.console_backend=none` forces the touch UI on glass; the `compose` value is meaningless now). Marked breaking for the store-screenshot surface: the Compose console's marketing scenes (console home, console settings, coverflow, connect takeover) are gone — the Skia shell renders over native GL and cannot compose under Roborazzi. Its shots come from the desktop screenshot dump (the same pixels by construction) or a device capture. Gates: :app:testDebugUnitTest + :kit:testDebugUnitTest green, installDebug on the NP3 and the console renders on glass (carousel, OS marks, legend, aurora — the desktop look). |
||
|
|
f3a69d5b56 |
build(android): the 32-bit ABI gets the Skia console too, from a self-hosted archive
WP6 of design/android-skia-console-port.md. rust-skia publishes prebuilt Skia archives for
aarch64/x86_64/i686-linux-android but not armv7-linux-androideabi, and the 32-bit TV boxes
(Chromecast-with-Google-TV class) are console-UI-always devices — so the archive is built once
with skia-bindings itself (FORCE_SKIA_BUILD=1 for pf-console-ui's gl+textlayout feature set:
1666 ninja steps, ~80 s on an M-series Mac) and packed in the exact skia-binaries/ layout the
downloader unpacks. Key a25a0fdb7d90429aa2d1-armv7-linux-androideabi-gl-jpegd-jpege-pdf-textlayout,
sha256 4867856b… (table in kit/build.gradle.kts beside the two GitHub keys).
- clients/android/native: the console host and its deps are on every Android ABI now
(the arch gate is gone; nativeConsoleAvailable() answers true on all three).
- kit/build.gradle.kts: forwards SKIA_BINARIES_URL (-PskiaBinariesUrl or the env) to every
cargo-ndk run — the {tag}/{key} template that serves all three keys from one mirror.
- .gitea/workflows/android.yml: the repo variable SKIA_BINARIES_URL feeds it.
Proven: on a clean target dir with the url template pointing at a local mirror, every ABI's
skia-bindings log says DOWNLOAD AND INSTALL SUCCEEDED (armv7 included), the full debug APK
builds and installs, and cargo ndk clippy -D warnings is green on all three ABIs.
⚠ Until the three archives are hosted (storage.unom, then set the CI variable), an armv7 build
without the url falls back to a Skia source build — minutes with ninja present, a red leg
without it. Hosting is the one step left in this WP.
|
||
|
|
6d1c7187d3 |
feat(android): the Skia console fronts the gamepad UI, behind a transition switch
WP4 + WP5 of design/android-skia-console-port.md. The Android client now hosts the same console shell the Linux/Windows session binary shows — drawn by native over EGL/GLES on a SurfaceView (the WP3 host) — and Kotlin keeps every service behind it. Kotlin (clients/android/app/.../console/): - SkiaConsole — the process-lifetime holder (the console's stack survives a stream, exactly as the desktop's does): host rows from KnownHostStore + discovery + the reachability sweep, the library pipeline (cache first, wake + retry across the boot window, catalog, running set, posters over mTLS as bytes), pairing via nativePair, the wake-and-wait loop, the settings round-trip (Settings ↔ trust::Settings JSON; console-owned keys persisted whole), OverlayAction::Launch → connectToHost with the host's profile / a pinned card's one-off / the request-access budget, CancelConnect, Quit → task to back, CopyText → clipboard, and punktfunk:// links (known-and-pinned dials; anything needing trust is a notice — a link never establishes trust). - SkiaConsoleShell — the composable: the SurfaceView + its lifecycle, safe-area insets (systemBars ∪ displayCutout, in surface px), a density floor under the couch scale for phones, MainActivity's pad probes feeding raw MenuSamples (buttons, stick, HAT) into the shared MenuNav, remote D-pad keys as discrete events, hardware keys as Keys + typed text, the system Back as B, touch as pointer input, haptic pulses, and the two Compose overlays the console can open (Controllers, Licences). - ConsoleJson — the wire, in the model types' own serde shapes. - App.kt picks SkiaConsoleShell over GamepadShell when SkiaConsole.wanted(): the native host exists (64-bit ABIs) and sysprop debug.punktfunk.console_backend != compose. The Compose console stays until the on-glass matrix and the armv7 archive land (WP7). - NativeBridge: the nativeConsole* externs. GameEntry.platform parsed + cached (Collections). pf-console-ui (WP5): the Android-only settings rows over Settings::extra `android.*` keys (low-latency decode, phone rumble/gyro, SC2 passthrough, DualSense capture, controller-UI mode) and two platform-screen action rows (Controllers, Licences) that raise the new ConsoleCmd::OpenPlatformScreen; row_on() keeps them off the desktop's list, which shows exactly the rows it did (pinned by test). "Send logs" hides where nothing serves it. pf-console-ui (test harness): a test-only fixed-step clock for the screenshot dump. The dump's aurora phase was wall-clock time, so two runs agreed only at equal machine load and speed — a pixel comparison of main against a candidate read as a regression that was pure drift. With the fixed step the same tree renders the same pixels anywhere. Gates: container fmt + clippy -D warnings + build + tests; cargo ndk clippy -D warnings on arm64-v8a / x86_64 / armeabi-v7a; :app:compileDebugKotlin; the fixed-clock dump of this tree vs main. |
||
|
|
84db02ef6d |
refactor(console-ui): the Skia console shell becomes portable, and Android grows a GL host for it
The shell that pf-console-ui draws for the Vulkan session binary is the console UI
the Android client should have been showing all along (design/android-skia-console-port.md).
This is WP1–WP3 of that plan: nothing the desktop does changes, and the same crate now
compiles for aarch64/x86_64-linux-android against rust-skia's prebuilt GL archives.
pf-client-core: the data the shell speaks is un-gated for android — trust::Settings and
the known-hosts store, the profiles model, deep links, the library MODEL (the ureq
fetches stay desktop), and three modules split out so the platform-bound ones can stay
platform-bound: audio_format (the AUDIO_FORMAT_* table, re-exported by session),
decoder_pref (migrate_decoder_pref, re-exported by video), menu_nav (MenuEvent /
MenuNav / PadInfo, re-exported by gamepad) and console (OverlayAction, PointerInput,
SessionPhase, re-exported by pf-presenter — pf-console-ui sits above pf-presenter, so the
types could not live there). MenuNav gains the stick hysteresis both the Apple and the
Android console had to grow on glass: an engaged direction holds until its own axis
falls below MENU_RELEASE (0.3), so a diagonal flick is one move, not two.
pf-console-ui: the Vulkan overlay + SDL event path move behind the default
`vulkan-overlay` feature (clients/session is unchanged); a `Key` enum replaces SDL
scancodes; a `SettingsStore` seam replaces the six direct settings-file sites (desktop =
the file, exactly as before; SnapshotStore for hosts across a language boundary);
`Viewport{width,height,insets,scale}` replaces bare w×h (zero insets + the couch formula
on the desktop = byte-identical layout); `Platform` filters the settings rows (the
desktop shows every row it did); ConsoleOptions carries the store, the platform and the
GPU cache budget; the portable `Console` driver is what both hosts hold. skia-safe's
features are target-specific so the desktop key stays jpegd-jpege-pdf-textlayout-vulkan
(the flatpak pin) and Android resolves gl-jpegd-jpege-pdf-textlayout. The model types
derive serde: the wire IS the model.
clients/android/native: src/console/ — a hand-declared EGL binding, Skia's GL
DirectContext over FBO 0, one render thread paced by eglSwapBuffers that owns the
console and applies Kotlin's input from a command queue, and ~28 nativeConsole* JNI
seams (surface lifecycle, pad samples through the shared MenuNav, pointer, keys, text,
session phases, model pushers, a blocking event poll, the command-bus drain). Gated to
the 64-bit ABIs: rust-skia publishes no armv7 archive and skia-bindings would silently
build Skia from source; nativeConsoleAvailable() tells Kotlin which.
Gates: container fmt + clippy -D warnings (all targets) + build + tests green
(pf-console-ui 180, pf-client-core 221); cargo ndk clippy -D warnings for
pf-client-core / pf-console-ui / punktfunk-client-android on arm64-v8a, x86_64 and
armeabi-v7a; skia-bindings logged DOWNLOAD AND INSTALL SUCCEEDED for both 64-bit keys.
|