Files
KwabaTV/docs/superpowers/specs/2026-09-16-kwabatv-rebuild-design.md
T
GGBandClaude Sonnet 5 12661077ff Add KwabaTV rebuild design spec
Rebuilds the deleted WebView-shell Android app, now pointed at the
kwaba-sync website's server-side TV mode instead of the desktop
site. Key changes from the original: room selection via a new
website /tv room-list route instead of a baked-in room ID, and the
Menu-button-to-popup mapping is already confirmed working on real
hardware ahead of any app code existing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHgWjmEJXsJEi1EVAoTp4K
2026-09-16 19:53:28 +02:00

167 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# KwabaTV Rebuild — Design
Date: 2026-09-16
Status: approved, pending implementation plan
## Background
The original `KwabaTV` Android app (package `de.ggb.kwabatv`) was a pure
WebView shell around the normal desktop Kwaba Sync site, mapping Fire TV
remote input to the site's existing desktop keyboard shortcuts via real
Android `KeyEvent`s (`webView.dispatchKeyEvent()`, not JS-simulated events —
Chromium requires a trusted user gesture for autoplay-unlock and
`requestFullscreen()`). Hardware testing on that app surfaced real layout/
focus-navigation problems (documented in `Claude outputs/kwaba-tv-mode-
handover.md`), which led to building a proper server-side TV mode directly
in the `kwaba-sync` website first (`/tv/:id`, a pinned 60/20/80 layout, a
menu popup, guarded shortcuts) rather than continuing to patch the app
against a desktop-shaped page. That work is done, deployed, and separately
specced in `kwaba-sync`'s own
`docs/superpowers/specs/2026-09-15-tv-mode-design.md`.
The original app's project folder was deleted. This spec covers rebuilding
it from scratch, now that it has a real TV-shaped page to point at instead
of the desktop site.
## Goal
A thin WebView-shell Android app for Fire TV / Android TV that loads
Kwaba Sync's TV-mode pages and forwards real remote-control key events to
them — reusing the desktop-shortcut-to-KeyEvent mechanism proven in the
original hardware test, retargeted at the new `/tv` routes and their
simplified key surface (individual per-action shortcuts mostly collapsed
into the one menu popup).
## Decisions made during this design pass
**New repo, new location.** `C:\Projekte\kwabatv`, its own git repo on the
same Gitea instance (`ssh://git@git.whodovoodoo.de:2222/GGB/KwabaTV.git`,
under the user's own account, not David's — this is a personal project,
distinct from contributing to `kwaba-sync`). Different tech stack (native
Kotlin/Android vs. the Node web repo), different release process (APK
sideload, no CONTRIBUTING.md deploy webhook), so it does not live inside
`kwaba-sync`.
**Room targeting: a new `/tv` room-list route, not a baked-in room ID.**
The obvious approach (bake a fixed room ID into `BuildConfig.WEBSITE_URL`,
matching the original app's fixed-URL design) was rejected: the user wants
to pick from multiple rooms via a DPAD-navigable list, not type a room ID
into a settings screen or rebuild the APK to change rooms. Rather than
build a native Android room-list screen (fetch `/api/rooms` directly,
render it with Android's own TV-focus system), the design extends the
website with one more server-side TV-mode route: `GET /tv`, a DPAD-
navigable room list in the same spirit as `/tv/:id` — arrow-key focus
between room cards, Enter/OK joins the focused one. This means:
- `BuildConfig.WEBSITE_URL` becomes `https://kwaba.whodovoodoo.pasteaf.de/tv`
— the room list, not a specific room. No per-build room configuration at
all.
- No native Android list/focus UI to write — the WebView shell handles
both screens identically, since both are server-rendered pages.
- Rejected alternative (native Android screen) would have meant real new
app-side UI code and a second navigation model (native list → WebView
room) instead of one consistent WebView-only flow.
**Reachable from inside a room too.** The existing TV-mode menu popup
(`#tvMenuOverlay` in `kwaba-sync`, built in the prior spec) gets a 7th
button, "Räume", linking to `/tv` — so leaving a room to pick another one
doesn't require the Back button's room-list navigation (below); it's also
reachable through the same menu used for Resync/Subtitles/Skip/Spoiler/
Mute/Fullscreen.
**Key mapping**, reusing the original's real-`KeyEvent` mechanism, retargeted:
| Remote input | Android event | Sent to WebView | Website behavior |
|---|---|---|---|
| DPAD OK/Center, Play/Pause | `KEYCODE_DPAD_CENTER` / `KEYCODE_MEDIA_PLAY_PAUSE` | `Space` | Play/Pause (and the one-time "Beitreten" tap) |
| DPAD Left/Right, tap | `KEYCODE_DPAD_LEFT/RIGHT`, `repeatCount==0` | `ArrowLeft`/`ArrowRight` | 5s seek |
| DPAD Left/Right, held | same keycodes, `repeatCount>0`, app-side throttled to ~1 event/200ms | `Shift+ArrowLeft/Right` | 30s seek. Android's own hardware key-repeat rate is faster than 200ms, so the app must throttle before forwarding — otherwise this floods the room's sync socket with seek commands broadcast to every viewer far faster than intended. |
| DPAD Up/Down, Prev/Next media keys | `KEYCODE_DPAD_UP/DOWN`, `KEYCODE_MEDIA_PREVIOUS/NEXT` | `P` / `N` | Prev/next title — deliberately not `ArrowUp/Down`: those are explicit no-ops under `body.tv` in the website (added specifically because volume is TV/AVR-controlled, not site-controlled) |
| Menu button | `KEYCODE_MENU` | `ContextMenu` | Opens the popup (Resync/Subtitles/Skip/Spoiler/Mute/Fullscreen/Räume). **Confirmed working**: the user tested a real Fire TV remote's Menu button against the deployed `/tv/:id` page (via a browser, ahead of this app existing) and the popup opened — the riskiest unknown in this design is already de-risked before any app code exists. |
| Back, in room + fullscreen | `KEYCODE_BACK` | `Escape` | Browser exits HTML5 fullscreen natively on Escape (standard browser/WebView behavior) — the page itself has no matching keydown case, which is fine, nothing else needs to happen. |
| Back, in room + not fullscreen | `KEYCODE_BACK` | *(not a key event)* | App navigates the WebView directly to `/tv` (room list) itself — native-side navigation, not something the website reacts to. Reuses fullscreen-state tracking the app needs anyway (see below) and the WebView's current-URL tracking to distinguish "in a room" from "on the room list". |
| Back, in room list | `KEYCODE_BACK` | *(consumed)* | Does nothing — blocks Android's default back-behavior, which would otherwise exit the app. |
| Home | `KEYCODE_HOME` | not intercepted | Native Fire TV home; app doesn't touch it. |
Fullscreen-state tracking (needed for the Back-button branch above) comes
from `WebChromeClient.onShowCustomView`/`onHideCustomView` — the standard
Android WebView hook fired when a page enters/exits HTML5 fullscreen via
`requestFullscreen()`. Any WebView that supports fullscreen video already
needs this implemented, so it's not new complexity, just something to
carry over correctly. "In a room vs. on the room list" comes from tracking
the WebView's currently-loaded URL path (`/tv` exactly vs. `/tv/<id>`) via
`onPageFinished`/`shouldOverrideUrlLoading`.
## Fixed decisions carried over unchanged from the original app
(From `Claude outputs/README.md` and the handover doc — still correct, not
revisited in this pass.)
- Package `de.ggb.kwabatv`, `compileSdk 34` / `minSdk 21` / `targetSdk 34`,
Gradle 8.7 + AGP 8.5.2 (the tested, working combination).
- `LEANBACK_LAUNCHER` manifest category for the Fire TV launcher entry.
- Standard `network_security_config.xml` (system trust anchors only) — the
domain runs Let's Encrypt, no certificate pinning needed.
- Reuse the existing branding assets already sitting in this session's
`Claude outputs/` folder (`ic_launcher_art.png` 512×512,
`banner_tv_art.png` 640×360) rather than regenerating them.
- Distribution stays APK sideload only — no store listing.
- Login is a one-time action: the WebView loads `/login` on first launch
if there's no session cookie yet; persistent cookie/DOM storage keeps
the session alive across app restarts. No native login screen.
- No chat, no mic/camera permission requests (`onPermissionRequest` →
`deny()`) — matches the website's own "no chat in TV mode" decision and
the fact that Fire TV Sticks typically have no camera anyway.
- Volume is not touched by the app; the website already forces its
internal volume to 100 in TV mode and the TV/AVR handles real volume.
## Explicitly out of scope for this pass
- The website's `/tv` room-list route and the popup's 7th "Räume" button
are **website changes**, made in the `kwaba-sync` repo, not this one —
tracked here only as a dependency this app needs, with its own
implementation plan on that side.
- Real further hardware quirks beyond the Menu-key mapping (already
confirmed working) — e.g. whether `KEYCODE_MEDIA_PREVIOUS/NEXT` are
actually sent by either of the user's two test remotes, exact fullscreen
visual behavior — are Task-5-style manual verification once the app is
built, not something to speculate about further in this spec.
- The two items already deferred in the website's own TV-mode spec (the
suspected buffer/sync feedback loop, and YouTube's native controls
showing despite `controls=0`) remain deferred; unrelated to the app.
## Anticipated file changes
**In `kwaba-sync`** (separate implementation plan on that side):
- `src/server.js` — new `GET /tv` route (room list)
- `public/` — new markup/CSS/JS for the room-list page (DPAD focus
navigation between cards), reusing existing branding/CSS tokens
- `public/room.html` + `public/js/room.js` — 7th "Räume" button in the
existing `#tvMenuOverlay`
**In this repo (`kwabatv`)**:
- Standard Android Studio project scaffold (`settings.gradle.kts`,
`build.gradle.kts`, Gradle wrapper pinned to 8.7)
- `app/src/main/java/de/ggb/kwabatv/MainActivity.kt` — WebView shell,
`dispatchKeyEvent` key-mapping table above, fullscreen tracking via
`WebChromeClient`, current-URL tracking for the Back-button branch
- `app/src/main/res/xml/network_security_config.xml`
- `app/src/main/AndroidManifest.xml` — `LEANBACK_LAUNCHER`, permissions
- `app/src/main/res/` — icon/banner drawables from the reused branding
assets
- `docs/superpowers/specs/` and `docs/superpowers/plans/` — this spec and
its implementation plan
## Testing approach
Real hardware is available this time — two devices (a TV remote and other
hardware with a Fire TV remote) — so verification is a direct build →
sideload → test loop rather than the browser-only approximation the
website work had to use. Per key-mapping row above:
- Confirmed already: Menu button → popup opens.
- To verify once the app exists: DPAD tap vs. hold seek timing/throttling,
Prev/Next media keys (not guaranteed to exist on every remote), Back-
button behavior in all three states (fullscreen / in-room / room-list),
Home behavior, fullscreen entry/exit visually, and cross-checking
behavior across both available remote/hardware combinations in case they
differ.