From 16839942b1bae3cba7b70bcdec18e1e4a8678c06 Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Sun, 12 Apr 2026 18:39:57 +0200 Subject: [PATCH] Focus play/pause button when player controls open --- .../components/TvPlayerControlsOverlayTest.kt | 32 ++++++++++++++++++- .../components/TvPlayerControlsOverlay.kt | 8 ++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app-tv/src/androidTest/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlayTest.kt b/app-tv/src/androidTest/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlayTest.kt index 9f51f471..308f6f77 100644 --- a/app-tv/src/androidTest/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlayTest.kt +++ b/app-tv/src/androidTest/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlayTest.kt @@ -48,6 +48,29 @@ class TvPlayerControlsOverlayTest { @get:Rule val composeRule = createAndroidComposeRule() + @Test + fun requestingControlsFocus_focusesPlayPauseInsteadOfSeekBar() { + composeRule.setContent { + AppTheme { + OverlayHost( + uiState = samplePlayerState(), + requestFocus = true + ) + } + } + + composeRule.waitForIdle() + + composeRule.onNodeWithTag(TvPlayerPlayPauseButtonTag).assertIsFocused() + composeRule.onNodeWithTag(TvPlayerSeekBarTag) + .assert( + SemanticsMatcher.expectValue( + SemanticsProperties.Focused, + false + ) + ) + } + @Test fun downFromSeekBar_movesFocusToControlButtonsBeforePlaylist() { composeRule.setContent { @@ -433,7 +456,8 @@ class TvPlayerControlsOverlayTest { @Composable private fun OverlayHost( uiState: PlayerUiState, - initiallyExpanded: Boolean = false + initiallyExpanded: Boolean = false, + requestFocus: Boolean = false ) { var isPlaylistExpanded by remember { mutableStateOf(initiallyExpanded) } val focusRequester = remember { FocusRequester() } @@ -441,6 +465,12 @@ private fun OverlayHost( val audioButtonFocusRequester = remember { FocusRequester() } val subtitlesButtonFocusRequester = remember { FocusRequester() } + LaunchedEffect(requestFocus) { + if (requestFocus) { + focusRequester.requestFocus() + } + } + Box(modifier = Modifier.size(width = 960.dp, height = 540.dp)) { TvPlayerControlsOverlay( uiState = uiState, diff --git a/app-tv/src/main/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlay.kt b/app-tv/src/main/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlay.kt index ce2fe2e3..27eea88a 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlay.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/tv/player/components/TvPlayerControlsOverlay.kt @@ -186,7 +186,6 @@ private fun TvPlayerBottomSection( modifier: Modifier = Modifier ) { val scheme = MaterialTheme.colorScheme - val playPauseFocusRequester = remember { FocusRequester() } val playlistFocusRequester = remember { FocusRequester() } var wasPlaylistExpanded by remember { mutableStateOf(isPlaylistExpanded) } @@ -194,7 +193,7 @@ private fun TvPlayerBottomSection( if (isPlaylistExpanded && uiState.queue.isNotEmpty()) { playlistFocusRequester.requestFocus() } else if (wasPlaylistExpanded) { - playPauseFocusRequester.requestFocus() + focusRequester.requestFocus() } wasPlaylistExpanded = isPlaylistExpanded } @@ -255,10 +254,9 @@ private fun TvPlayerBottomSection( onSeekRelative = onSeekRelative, togglePlayState = onPlayPause, onMoveDown = { - playPauseFocusRequester.requestFocus() + focusRequester.requestFocus() true }, - focusRequester = focusRequester, modifier = Modifier.testTag(TvPlayerSeekBarTag) ) Spacer(modifier = Modifier.height(8.dp)) @@ -292,7 +290,7 @@ private fun TvPlayerBottomSection( onClick = onPlayPause, size = 72, modifier = expandPlaylistModifier - .focusRequester(playPauseFocusRequester) + .focusRequester(focusRequester) .testTag(TvPlayerPlayPauseButtonTag) ) TvIconButton(