Repository navigation
Conversation
Space/K play-pause, J/L and arrows seek, up/down volume, M mute, 0-9 percent seek, Home/End, Shift+,/. speed, Shift+N/P episode, I picture-in-picture.
Skip shortcuts before the player is ready, while casting and while controls are locked; disable seeking and episode skips for live TV and intros; only skip episodes when a neighbour exists; match < and > by character so speed keys work on non-US layouts.
Track keyboard focus on the player root and dispatch keys to a focused control first, so panel lists, sliders and buttons keep arrow/Space navigation. Also toggle play/pause from UI state, map volume steps exactly, respect NumLock for numpad digits and ignore speed keys during a long-press speed-up.
Each repeated seek is forwarded to the SyncPlay server and re-syncs every member, so held J/L/arrow keys only seek once per press while in a group.
On live TV and intros, seek shortcuts are consumed as no-ops instead of falling through to focus navigation. Space/K pause while buffering, and shortcuts stay off while a cast session is connected.
Shortcuts step aside while an in-window panel (tracks, quality, speed, episodes, chapters, sleep timer, version picker) is open, and until an item is loaded. With nothing focused, OK/Enter shows the controls and then moves focus into them, so remotes and gamepads without Tab can still reach every control.
Remotes, gamepads and D-pads keep master's plain focus navigation, so drop the OK-to-Tab focus entry (it landed focus on Back, where the next Space exited the player). Also treat the SyncPlay members popup as an open panel, and resume instead of pausing once playback has ended.
Derive the open-panel flag from the same conditions the panels render under, so a panel that disappears with its data no longer disables shortcuts. When a control is focused, arrow keys stay with focus navigation even at its edge instead of seeking or changing volume.
CEC remote keys arrive via the virtual alphabetic keyboard, so require a physical keyboard and skip SOURCE_HDMI. Gate on left Alt only so AltGr layouts can still type < and >.
Some TV remotes report as full keyboards. Arrow keys now keep master's focus navigation until the same device presses a letter or Space in the player, after which they seek and change volume.
Space no longer clicks a focused control (e.g. Skip Outro); it toggles play/pause and shows the controls. Enter presses the visible skip button. Digits and Home/End now also need a device that has typed, and the volume indicator reads 0 when muted.
Consume the UP of every DOWN a shortcut took, even if state changed in between, so Compose never sees an unpaired UP. Leave I unhandled when picture-in-picture isn't supported.
The skip-segment button can hold focus as the only focusable control, which swallowed arrow keys. Once a keyboard has typed, arrows (like Space) always seek and change volume; other keys still reach a focused control first.
Owner
|
@polferov is this ready for testing? |
Author
|
Yes. I've been using it for the last few days without problems. |
polferov
marked this pull request as ready for review
October 8, 2026 09:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds keyboard shortcuts to the video player. Before this, a physical keyboard did nothing during playback; for example, Space did not pause on a tablet with a keyboard attached. The keymap copies the YouTube web player's shortcuts. The keys are handled in
PlayerActivity.dispatchKeyEvent, so they work with both ExoPlayer and mpv. Every action goes through an existingPlayerEventor ViewModel call, so sync-play, sleep-timer resume and the on-screen indicators behave exactly as they do with touch controls.Changes
Player: keyboard shortcuts (
PlayerActivity.kt)</>Which input gets shortcuts. Only physical full (alphabetic) keyboards. Remotes, gamepads, D-pads, HDMI-CEC remote input and virtual devices behave exactly as on
master.Keys that wait for typing. Arrows, digits and Home/End only become shortcuts after that same keyboard has pressed a letter or Space in the player. Until then they keep
master's behaviour. Some TV remotes report as full keyboards and have arrow and number keys, so this keeps them working as before.When shortcuts are off. Keys go to the framework untouched when:
Live TV and intros. Seeking and episode skips are disabled there. Their keys are consumed but do nothing, so they don't turn into focus moves.
Focus. If a control has keyboard focus (after Tab), it gets each key first, except Space. Arrow keys stay with focus navigation even at the edge of the controls.
Key repeat. Holding a seek or volume key repeats it. Inside a SyncPlay group, held seek keys fire once per press, so they don't flood the server with seeks.
Panel-open signal (
PlayerControls.kt,PlayerScreen.kt,PlayerViewModel.kt)PlayerControlsreports whether a panel is open through a newonPanelOpenChangecallback. The check uses the same conditions each panel renders under.PlayerScreenadds the version picker and writes the result toPlayerViewModel.isOverlayPanelOpen, which the activity reads.Test plan
./gradlew :app:compileDebugKotlin: passes./gradlew :app:testDebugUnitTest: passes./gradlew :app:assembleDebug: passes./gradlew ktfmtCheck: none of the changed files is newly flagged. The task fails on 19 files, an identical list onmaster.PlayerViewModel.ktis on that list already, and its new lines are kept out of the formatter's rewrite../gradlew checkCancellationRethrow: fails on 2 sites inAudiobookshelfProgressSyncer.ktthat fail identically onmaster. This diff doesn't touch them.Risk
onNextEpisode/onPreviousEpisode/SetPlaybackSpeed. Like the on-screen buttons and the speed panel, these act locally only inside a SyncPlay group.