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 09842dd0..b625ee00 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 @@ -56,6 +56,7 @@ import org.junit.Test private const val TvPlayerRootTag = "tv_player_root" private const val TvPlayerHiddenPauseCountTag = "tv_player_hidden_pause_count" private const val TvPlayerHiddenResumeCountTag = "tv_player_hidden_resume_count" +private const val TvPlayerHiddenSeekDeltaTag = "tv_player_hidden_seek_delta" @OptIn(ExperimentalTestApi::class) class TvPlayerControlsOverlayTest { @@ -492,6 +493,66 @@ class TvPlayerControlsOverlayTest { ) } + @Test + fun hiddenControls_leftSeeksWithoutShowingControls() { + composeRule.setContent { + AppTheme { + TrackPanelHost( + uiState = samplePlayerState(), + initialControlsVisible = false + ) + } + } + + composeRule.onNodeWithTag(TvPlayerRootTag) + .performSemanticsAction(SemanticsActions.RequestFocus) + .performKeyInput { + pressKey(Key.DirectionLeft) + } + + composeRule.waitForIdle() + + composeRule.onAllNodesWithTag(TvPlayerPlayPauseButtonTag).assertCountEquals(0) + composeRule.onAllNodesWithTag(TvPlayerHiddenStopFeedbackTag).assertCountEquals(0) + composeRule.onNodeWithTag(TvPlayerHiddenSeekDeltaTag) + .assert( + SemanticsMatcher.expectValue( + SemanticsProperties.StateDescription, + "-10000" + ) + ) + } + + @Test + fun hiddenControls_rightSeeksWithoutShowingControls() { + composeRule.setContent { + AppTheme { + TrackPanelHost( + uiState = samplePlayerState(), + initialControlsVisible = false + ) + } + } + + composeRule.onNodeWithTag(TvPlayerRootTag) + .performSemanticsAction(SemanticsActions.RequestFocus) + .performKeyInput { + pressKey(Key.DirectionRight) + } + + composeRule.waitForIdle() + + composeRule.onAllNodesWithTag(TvPlayerPlayPauseButtonTag).assertCountEquals(0) + composeRule.onAllNodesWithTag(TvPlayerHiddenStopFeedbackTag).assertCountEquals(0) + composeRule.onNodeWithTag(TvPlayerHiddenSeekDeltaTag) + .assert( + SemanticsMatcher.expectValue( + SemanticsProperties.StateDescription, + "10000" + ) + ) + } + @Test fun backOnTrackPanel_closesItAndRestoresFocusToOpeningButton() { composeRule.setContent { @@ -656,6 +717,7 @@ private fun TrackPanelHost( var stopFeedbackRequestId by remember { mutableStateOf(0) } var pauseWithoutControlsCount by remember { mutableStateOf(0) } var resumeWithoutControlsCount by remember { mutableStateOf(0) } + var lastHiddenSeekDeltaMs by remember { mutableStateOf(0L) } val rootFocusRequester = remember { FocusRequester() } val controlsFocusRequester = remember { FocusRequester() } val qualityButtonFocusRequester = remember { FocusRequester() } @@ -677,6 +739,10 @@ private fun TrackPanelHost( resumeWithoutControlsCount += 1 stopFeedbackVisible = false } + val seekWithoutShowingControls: (Long) -> Unit = { deltaMs -> + lastHiddenSeekDeltaMs = deltaMs + stopFeedbackVisible = false + } LaunchedEffect(trackPanelType, pendingTrackButtonFocus) { val pendingFocus = pendingTrackButtonFocus ?: return@LaunchedEffect @@ -721,7 +787,7 @@ private fun TrackPanelHost( onHideControls = { controlsVisible = false }, onPausePlaybackWithoutShowingControls = pausePlaybackWithoutShowingControls, onResumePlaybackWithoutShowingControls = resumePlaybackWithoutShowingControls, - onSeekRelative = { _ -> controlsVisible = true }, + onSeekRelative = seekWithoutShowingControls, onShowControls = { controlsVisible = true }, onTogglePlayPause = { controlsVisible = true } ) @@ -781,6 +847,12 @@ private fun TrackPanelHost( .semantics { stateDescription = resumeWithoutControlsCount.toString() } ) + Box( + modifier = Modifier + .testTag(TvPlayerHiddenSeekDeltaTag) + .semantics { stateDescription = lastHiddenSeekDeltaMs.toString() } + ) + trackPanelType?.let { panelType -> TvTrackSelectionPanel( panelType = panelType, diff --git a/app-tv/src/main/java/hu/bbara/purefin/tv/player/TvPlayerScreen.kt b/app-tv/src/main/java/hu/bbara/purefin/tv/player/TvPlayerScreen.kt index 6df95958..a63bc6c1 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/tv/player/TvPlayerScreen.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/tv/player/TvPlayerScreen.kt @@ -150,6 +150,10 @@ fun TvPlayerScreen( viewModel.seekBy(deltaMs) showTvControls() } + val seekByWithoutShowingControls: (Long) -> Unit = { deltaMs -> + viewModel.seekBy(deltaMs) + stopFeedbackVisible = false + } val seekToLiveEdgeAndShowControls: () -> Unit = { viewModel.seekToLiveEdge() showTvControls() @@ -226,7 +230,7 @@ fun TvPlayerScreen( onHideControls = { viewModel.toggleControlsVisibility() }, onPausePlaybackWithoutShowingControls = pausePlaybackWithoutShowingControls, onResumePlaybackWithoutShowingControls = resumePlaybackWithoutShowingControls, - onSeekRelative = seekByAndShowControls, + onSeekRelative = seekByWithoutShowingControls, onShowControls = showTvControls, onTogglePlayPause = togglePlayPauseAndShowControls )