feat(companion): retain audio with native media controls
This commit is contained in:
@@ -5,6 +5,8 @@ candidate worktree is `/home/archipelago/Projects/archy-session-key` on branch
|
||||
`work/post-190-session-key`. Do not use credentials from chat or print private
|
||||
node, wallet, session or registry data.
|
||||
|
||||
Latest active checkpoint: see **Companion56 native-audio qualification** at the end. Earlier receipts below are historical and apply only to their named source/artifacts.
|
||||
|
||||
## Current candidate
|
||||
|
||||
Recent commits, newest first:
|
||||
@@ -289,7 +291,7 @@ No percentage is inferred from test counts.
|
||||
| 17 HTTPS apps | In progress | Exact hostname/trust and companion acceptance |
|
||||
| 18 Firewall/tunnel | In progress | Read-only UI done; settings persistence/reboot/rollback qualification |
|
||||
| 19 Media guide/Cloud PiP | Nearly finished | PiP operator-accepted; finish reusable contract and remaining edge cases |
|
||||
| 20 Native background media | In progress | Cloud PiP done; native audio service/controls and device checks remain |
|
||||
| 20 Native background media | In progress | Native service/controls implemented and APK56 delivered; matching LAN HTTP UI qualification and physical device checks remain |
|
||||
| 21 Public files/folders | Queued, before22 | Shared pricing interface with blue tints, unrelated-node discovery and tiny marker |
|
||||
| 22 Mesh file sharing | Queued, final task | Node/computer choice, tree/search picker, retained multi-selection, delivery design and usual paid-file flow |
|
||||
|
||||
@@ -327,3 +329,50 @@ Task22 now follows task21 at the end. Its detailed scope is in
|
||||
`post-1.9.0-work-backlog.md`. This is a recorded requirement/design task, not a
|
||||
claim that the picker or transport/payment flow has been implemented. Existing
|
||||
public-sharing blue pricing and marker requirements are retained unchanged.
|
||||
|
||||
|
||||
## Companion56 native-audio qualification — 2026-10-07
|
||||
|
||||
- Operator withdrew the accidental screensaver/Play/Skip prompts. No changes
|
||||
were made for them, so nothing required reverting.
|
||||
- Companion0.5.36/build56 is delivered at
|
||||
`http://192.168.63.240/packages/archipelago-companion-0.5.36.apk`.
|
||||
SHA256 `6bee2a2a13231525997946926bb47c473e01b81c03737d22d1bce18af9982c6e`.
|
||||
Canonical signing, clean packaging and HTTP byte verification passed. The
|
||||
standard fleet APK and live backend/catalogs were preserved.
|
||||
- 29 Android tests pass (including native bridge, foreground notification and
|
||||
task-removal retention on SDK28/35). These are automated tests, not phone
|
||||
acceptance. Durable receipt:
|
||||
`~/.local/state/archipelago/release-qualification/companion-056/`.
|
||||
- The native MediaSession/foreground service controls the same retained
|
||||
authenticated WebView player, with queue controls, bounded thumbnail data,
|
||||
session/origin checks and logout/stop cleanup. No protected stream URLs or
|
||||
credentials are sent to Android and no second decoder/payment is started.
|
||||
- Initial complete dashboard qualification passed1440 tests/175files and its
|
||||
production build. That UI was deployed on dev with backup and unchanged
|
||||
backend, catalog/session/intent hashes and container IDs/start times.
|
||||
- Live LAN HTTP browser inspection then found `crypto.randomUUID` absent. The
|
||||
new bridge now generates UUIDv4 using `crypto.getRandomValues`, which that
|
||||
browser supports. A regression covers this exact condition; final full UI
|
||||
suite/build and replacement deployment are underway. Do not ask the operator
|
||||
to accept native audio until the matching corrected Yaya UI is deployed.
|
||||
- Yaya remains at its previous UI until qualification completes. Physical
|
||||
Android background, lock-screen, swipe-away, reconnect and stop acceptance
|
||||
remains required. CloudPiP55 remains operator accepted.
|
||||
- Dedicated IndeeHub agent owns backend changes and the full isolated seven-app
|
||||
fixture. Its first latest suite had2002pass/1failure/5ignored: the new fixture
|
||||
incorrectly expected no directory, although an empty directory is valid.
|
||||
Assertion corrected to require no journal files. Final isolated recheck:
|
||||
**2,003 passed, zero failed, five ignored**, committed as `b9c75b21`. This
|
||||
is not a live IndeeHub delivery; optimized binary and full VM cutover still
|
||||
remain before Yaya activation.
|
||||
|
||||
- First HTTP-fix full UI run:1440 passed, one60-second Home wallet-display
|
||||
timeout and a worker `snapshotSaved` timeout under severe memory/I/O pressure.
|
||||
Focused rerun:all12 Home wallet-cache tests and all6 companion bridge tests
|
||||
passed (18/18). Clean full rerun is underway; retain both failed and passing
|
||||
logs. The initial1440-green full receipt predates the UUID fix.
|
||||
- Extended real V4V browser qualification supports optional simulated Android
|
||||
transport while using the actual authenticated iframe and bundled free demo
|
||||
media. It checks native command routing and JPEG thumbnail delivery; no
|
||||
physical Android result is implied. The new end-to-end run remains pending.
|
||||
|
||||
@@ -7,10 +7,12 @@ reference app; its updated real-node acceptance is tracked in
|
||||
[v4v-native-player-20261006.md](v4v-native-player-20261006.md). Local tests are not
|
||||
proof that a particular deployed app or companion version supports every feature.
|
||||
|
||||
Cloud video picture-in-picture (PiP) in the Android companion is a separate open
|
||||
implementation task. Do not advertise the audio bridge as a video/PiP API. Native
|
||||
fullscreen support alone does not provide Android PiP. A versioned video contract
|
||||
and examples must be added here when implementation and device tests pass.
|
||||
Cloud video PiP is implemented in companion0.5.35/build55; the operator accepted
|
||||
the delivered Cloud PiP flow on2026-10-07. Audio and video use separate channels.
|
||||
Native background audio is a companion0.5.36/build56 candidate:29 Android tests
|
||||
and six companion-bridge dashboard tests pass; physical background/lock-screen/task
|
||||
removal acceptance remains required. Do not infer those phone results from
|
||||
browser or Robolectric tests.
|
||||
|
||||
## Declare the audio integration
|
||||
|
||||
@@ -152,7 +154,7 @@ those guards merely to make a test pass.
|
||||
viewport test cannot prove OS background playback or PiP support. Do not promise
|
||||
playback after the operating system kills the process.
|
||||
|
||||
## Cloud video PiP acceptance scope (not yet implemented)
|
||||
## Cloud video PiP contract and remaining acceptance
|
||||
|
||||
Start with Cloud's existing authorized video viewer and companion fullscreen host.
|
||||
Provide an explicit PiP control when supported, with clear unavailable behavior.
|
||||
@@ -165,3 +167,67 @@ logout/session expiry, FIPS disconnect/reconnect, and process recreation. Valida
|
||||
on a physical companion with ordinary and FIPS-accessed Cloud videos before APK
|
||||
publication. Document the proven video integration contract for other app authors
|
||||
only after that implementation is qualified.
|
||||
|
||||
|
||||
The dashboard uses the main-frame-only `ArchipelagoCloudVideo` channel, admitted
|
||||
only for the paired node's exact HTTP(S) origins. Its version1 capability check
|
||||
is separate from `archipelago-v1` app audio integration. The Cloud host arms a
|
||||
random request/session ID with video dimensions and playing state, then enters
|
||||
fullscreen on the existing video in the same user gesture before requesting PiP.
|
||||
Native commands and replies carry that session; a different session is ignored.
|
||||
`state` updates playback controls and `release` retires the session. `restored`
|
||||
returns to the same video; closing requests pause/cleanup. No video URL, cookie,
|
||||
bearer token or second player crosses the channel. The entire dashboard must
|
||||
never be used as the PiP surface. This is currently a Cloud-host contract, not
|
||||
permission for arbitrary embedded apps to call native PiP directly.
|
||||
|
||||
## Companion native audio: build56 candidate
|
||||
|
||||
Apps continue using the existing version1 app audio protocol above. They do not
|
||||
need a second Android stream or a separate queue implementation. The dashboard
|
||||
owns a main-frame `ArchipelagoAudio` channel restricted to paired node origins;
|
||||
embedded app frames cannot invoke it directly. The foreground `mediaPlayback`
|
||||
service owns the retained WebView session after the Activity/task closes and
|
||||
exposes an Android MediaSession with playback metadata, play/pause, previous/next,
|
||||
seek, shuffle and Stop. Playback state is confirmed by the existing app player,
|
||||
not optimistically advanced by native controls.
|
||||
|
||||
Dashboard→native messages contain version1, a random session, monotonically
|
||||
increasing sequence, bounded title/position/duration, playback/control flags and
|
||||
optional JPEG thumbnail bytes. Artwork is fetched by the already authenticated
|
||||
page with same-origin credentials only, capped at512KiB, resized to192px and sent
|
||||
as a bounded data URL. Native code does not fetch artwork URLs or receive auth
|
||||
credentials. Cross-origin images without CORS may have no notification thumbnail;
|
||||
missing artwork never blocks playback. Native image decoding bounds dimensions.
|
||||
|
||||
Native→dashboard controls carry the same session. Retired sessions, stale sequence
|
||||
numbers, foreign origins and subframes cannot revive/control playback. A short
|
||||
heartbeat resynchronizes state; if the dashboard stops responding for90seconds,
|
||||
the native owner stops instead of advertising a live session indefinitely.
|
||||
Playback stays in the same authenticated WebView/iframe; app authorization and
|
||||
entitlement checks remain with that player. App removal, frame replacement,
|
||||
logout, navigation/disconnect or explicit Stop release the corresponding session.
|
||||
A non-playing task removed from Recents is released. A playing one is retained;
|
||||
reopening reattaches the existing document, queue and position. A killed process
|
||||
is not automatically restarted into an authenticated stream.
|
||||
|
||||
The implementation uses the platform MediaSession with the existing WebView
|
||||
player, rather than adding another decoder. No boot receiver or new storage
|
||||
permission is involved. Pair this APK with the matching dashboard build: an older
|
||||
dashboard does not send the native audio protocol merely because the APK changed.
|
||||
|
||||
Qualification commands:
|
||||
|
||||
```sh
|
||||
cd neode-ui
|
||||
./node_modules/.bin/vitest run src/composables/__tests__/useCompanionAudio.test.ts src/composables/__tests__/useAppMediaBridge.test.ts src/components/__tests__/GlobalAudioPlayerExternal.test.ts
|
||||
cd ../Android
|
||||
./gradlew :app:testDebugUnitTest
|
||||
```
|
||||
|
||||
Before marking physical acceptance, use V4V on the matching server: play a track,
|
||||
close its panel, press Home, lock the phone, pause/resume/seek/skip/shuffle from
|
||||
native controls, remove the companion task while playing, reopen into the same
|
||||
queue/position, then Stop. Repeat logout, headset disconnect and network loss.
|
||||
Confirm download, fullscreen and Cloud PiP still work. App force-stop/process
|
||||
kill is a stop condition, not a promise of uninterrupted playback.
|
||||
|
||||
Reference in New Issue
Block a user