feat(player): answer transport keys with transient indicators, not the chrome

Pressing pause or seeking while the player's on-screen controls were hidden raised
the entire OSD, covering the subtitles the viewer was rewinding to read. Transport
keys now answer with a transient indicator and leave the chrome down; Select,
D-pad Center and a centre tap remain the deliberate way to bring the controls
back.

Play/pause confirms with an icon-only translucent disc at the centre of the frame,
72px around a 44px glyph, which grows and fades in, holds half a second at rest,
then runs the same motion in reverse. Seeking shows the amount plus a single
chevron on the same line at the edge it travels toward, with no backdrop at all:
anything large enough to read as a surface is large enough to cover picture and
subtitles, so legibility comes from shadows instead. Only the chevron moves, and
it eases outward across most of its cycle and returns briefly, holding a visible
opacity floor rather than blinking out. Type is scaled per platform, since a
television is read from across the room. The existing text pill stays for genuine
notices - rate changes, chapter titles, zoom, errors - because an earlier centred
pill overlapped ASS \an8 subtitle placement, which is the readability complaint
this feedback exists to answer.

Every relative seek entry point now shares one coalescing primitive. The keyboard
shortcuts fell through to KeyboardShortcutsService and previously reported
nothing, and both they and the remote's chapter fallback rebased each press off
player.state.position, so a burst against a slow backend pinned every request near
one step while the indicator climbed to a total that was never committed. A
released key commits its pending target immediately and resets the acceleration
tier, including on live TV where seeks bypass the accumulator. A chapter seek with
nowhere to go, past the last chapter or already at the start, no longer announces a
jump it does not perform.

Rewind-on-resume follows the resolved intent rather than the current state, so a
directed pause on an already-paused video neither resumes nor rewinds. Indicators
carry their own liveRegion semantics nodes: their labels previously merged into the
full-screen "show playback controls" target, corrupting its accessible name, and
they keep announcing "Paused"/"Playing" and the seek amount from icon-only visuals.

close #1676
This commit is contained in:
edde746
2026-07-29 04:24:04 +02:00
parent 0eee9f688d
commit f3795d49eb
38 changed files with 1719 additions and 181 deletions
@@ -23,17 +23,40 @@ tags:
- extendedWaitUntil:
notVisible: "Pause"
timeout: 10000
# Hardware transport keys drive playback without raising the chrome (#1676):
# the player answers with a centred disc (pause) or a side readout (seek), never the
# full chrome, so subtitles stay readable.
# Pause first — it also freezes the clip, so the seek assertions below cannot
# race the end of a short fixture item.
- pressKey: "Remote Media Play Pause"
- extendedWaitUntil:
visible: "Play"
visible: "Paused"
timeout: 10000
- assertNotVisible: "(?s)^Play$"
# D-pad seeking reports through the skip badge, never the scrub bar, and
# consecutive presses in one direction stack into a running total.
- pressKey: "Remote Dpad Right"
- extendedWaitUntil:
visible: "(?s)^Seek forward 10 seconds$"
timeout: 10000
- assertNotVisible: "(?s)^Play$"
- pressKey: "Remote Dpad Right"
- extendedWaitUntil:
visible: "(?s)^Seek forward 20 seconds$"
timeout: 10000
- pressKey: "Remote Media Play Pause"
- extendedWaitUntil:
visible: "Pause"
visible: "Playing"
timeout: 10000
- assertNotVisible: "(?s)^Pause$"
# Select stays the deliberate way to bring the chrome back.
- pressKey: "Remote Dpad Center"
- extendedWaitUntil:
visible: "(?s)^(Play|Pause)$"
timeout: 10000
- pressKey: "back"
- extendedWaitUntil:
notVisible: "Pause"
notVisible: "(?s)^Pause$"
timeout: 10000
- assertNotVisible: "Overview"
- pressKey: "back"