github bilbospocketses/abs-app android-tv-v1.0.3
Android TV v1.0.3

Connect Page D-pad Navigation Overhaul

What started as a quick visual tweak to the server/user selection screen turned into a deep dive into Android TV WebView D-pad focus management. Every change below exists so that D-pad navigation on this screen feels completely intuitive — no user guide needed.

Visual Improvements

  • Focus box wraps username + server URL — both lines are now inside a single focusable container, so the D-pad highlight covers the full entry
  • Username matches server URL styling — same font size and color (text-base text-fg) instead of the previous text-lg text-white font-bold
  • Focus box breathing room — 6px symmetric padding on left/right edges prevents text cutoff at the focus ring boundary
  • GitHub links scrollable on TV — changed from position: fixed to normal document flow so D-pad can actually reach and scroll to them

D-pad Navigation

  • Full left/right sub-icon chain: Entry row →right→ ellipsis →right→ delete, then delete →left→ ellipsis →left→ back to entry row
  • Up/down always lands on the entry row, never on an adjacent row's icon — cross-row icon focus is intercepted and redirected
  • First entry scroll-to-top — focusing the first server entry always scrolls the page to the top, keeping the back arrow visible and reachable

Bugs Fixed

  • Double-jump on up/down from icons — native D-pad engine and programmatic focus were both moving focus, causing rows to be skipped
  • Bounce on right from ellipsis to delete — keydown events bubbling from icons to the parent entry row caused competing deferred focus calls
  • Left from ellipsis stuck — native D-pad cannot navigate from a child element to its parent; explicit handler added
  • Page oscillation on row 1 — native focus-scroll behavior caused the viewport to jitter when cycling between icons; all focus calls now save/restore scroll position
  • Left from entry row entering children — native D-pad was entering the row's children from the right side, landing on the delete icon

Technical Approach

The Android TV WebView's native D-pad spatial navigation has fundamental limitations inside nested focusable containers. The solution layers five mechanisms:

  1. dpadFocus() — defers .focus({ preventScroll: true }) via setTimeout(0) to execute after the native engine finishes; saves/restores scroll position
  2. handleIconFocus() (@focus) — intercepts cross-row focus on icons and redirects to the entry row; respects _dpadFocusActive flag to avoid interfering with in-flight deferred operations
  3. .prevent.stop on icon keydown — prevents both default behavior and event bubbling to the parent entry row
  4. lockScroll() — prevents scroll oscillation on dead-end key presses (e.g., right from delete icon)
  5. onEntryRowFocus() — deferred scroll-to-top when first entry receives focus

Documentation updated in TV_FOCUS_SYSTEM.md with full D-pad navigation map and mechanism table.

Don't miss a new abs-app release

NewReleases is sending notifications on new releases.