From b41a9c24a2c3643a6a931b9758758053740e2f25 Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Wed, 8 Apr 2026 20:19:04 +0200 Subject: [PATCH] feat: Implement auto-hide delay for TV player controls --- .../bbara/purefin/tv/player/TvPlayerScreen.kt | 55 ++++++++++++++----- .../core/player/viewmodel/PlayerViewModel.kt | 29 +++++----- 2 files changed, 56 insertions(+), 28 deletions(-) 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 9c6d756f..f5af5ae8 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 @@ -45,6 +45,8 @@ import hu.bbara.purefin.tv.player.components.TvPlayerQueuePanel import hu.bbara.purefin.tv.player.components.TvTrackPanelType import hu.bbara.purefin.tv.player.components.TvTrackSelectionPanel +private const val TV_CONTROLS_AUTO_HIDE_MS = 5_000L + @OptIn(UnstableApi::class) @Composable fun TvPlayerScreen( @@ -82,12 +84,36 @@ fun TvPlayerScreen( LaunchedEffect(uiState.isPlaying) { if (uiState.isPlaying && controlsVisible) { - viewModel.toggleControlsVisibility() + viewModel.showControls(TV_CONTROLS_AUTO_HIDE_MS) } } val hiddenControlFocusRequester = remember { FocusRequester() } val controlsFocusRequester = remember { FocusRequester() } + val showTvControls: () -> Unit = { + viewModel.showControls(TV_CONTROLS_AUTO_HIDE_MS) + } + val togglePlayPauseAndShowControls: () -> Unit = { + viewModel.togglePlayPause(TV_CONTROLS_AUTO_HIDE_MS) + } + val seekAndShowControls: (Long) -> Unit = { positionMs -> + viewModel.seekTo(positionMs) + showTvControls() + } + val seekByAndShowControls: (Long) -> Unit = { deltaMs -> + viewModel.seekBy(deltaMs) + showTvControls() + } + val seekToLiveEdgeAndShowControls: () -> Unit = { + viewModel.seekToLiveEdge() + showTvControls() + } + val nextAndShowControls: () -> Unit = { + viewModel.next(TV_CONTROLS_AUTO_HIDE_MS) + } + val previousAndShowControls: () -> Unit = { + viewModel.previous(TV_CONTROLS_AUTO_HIDE_MS) + } LaunchedEffect(controlsVisible) { if (controlsVisible) { @@ -106,23 +132,22 @@ fun TvPlayerScreen( if (!controlsVisible && event.type == KeyEventType.KeyDown) { when (event.key) { Key.DirectionLeft -> { - viewModel.seekBy(-10_000) + seekByAndShowControls(-10_000) true } Key.DirectionRight -> { - viewModel.seekBy(10_000) + seekByAndShowControls(10_000) true } Key.DirectionUp, Key.DirectionDown -> { - viewModel.showControls() + showTvControls() true } Key.DirectionCenter, Key.Enter -> { - viewModel.togglePlayPause() - viewModel.showControls() + togglePlayPauseAndShowControls() true } @@ -157,12 +182,12 @@ fun TvPlayerScreen( modifier = Modifier.fillMaxSize(), uiState = uiState, focusRequester = controlsFocusRequester, - onPlayPause = { viewModel.togglePlayPause() }, - onSeek = { viewModel.seekTo(it) }, - onSeekRelative = { viewModel.seekBy(it) }, - onSeekLiveEdge = { viewModel.seekToLiveEdge() }, - onNext = { viewModel.next() }, - onPrevious = { viewModel.previous() }, + onPlayPause = togglePlayPauseAndShowControls, + onSeek = seekAndShowControls, + onSeekRelative = seekByAndShowControls, + onSeekLiveEdge = seekToLiveEdgeAndShowControls, + onNext = nextAndShowControls, + onPrevious = previousAndShowControls, onOpenAudioPanel = { trackPanelType = TvTrackPanelType.AUDIO }, onOpenSubtitlesPanel = { trackPanelType = TvTrackPanelType.SUBTITLES }, onOpenQualityPanel = { trackPanelType = TvTrackPanelType.QUALITY }, @@ -174,10 +199,10 @@ fun TvPlayerScreen( modifier = Modifier.align(Alignment.Center), uiState = uiState, onRetry = { viewModel.retry() }, - onNext = { viewModel.next() }, + onNext = nextAndShowControls, onReplay = { viewModel.seekTo(0L) - viewModel.togglePlayPause() + togglePlayPauseAndShowControls() }, onDismissError = { viewModel.clearError() } ) @@ -209,7 +234,7 @@ fun TvPlayerScreen( TvPlayerQueuePanel( uiState = uiState, onSelect = { id -> - viewModel.playQueueItem(id) + viewModel.playQueueItem(id, TV_CONTROLS_AUTO_HIDE_MS) showQueuePanel = false }, onClose = { showQueuePanel = false }, diff --git a/core/player/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt b/core/player/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt index 4e3cf253..71aa3fc2 100644 --- a/core/player/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt +++ b/core/player/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt @@ -29,6 +29,9 @@ class PlayerViewModel @Inject constructor( private val mediaRepository: MediaRepository, private val progressManager: ProgressManager ) : ViewModel() { + companion object { + private const val DEFAULT_CONTROLS_AUTO_HIDE_MS = 3_500L + } val player get() = playerManager.player @@ -176,9 +179,9 @@ class PlayerViewModel @Inject constructor( } } - fun togglePlayPause() { + fun togglePlayPause(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) { playerManager.togglePlayPause() - showControls() + showControls(autoHideDelayMs) } fun seekTo(positionMs: Long) { @@ -193,33 +196,33 @@ class PlayerViewModel @Inject constructor( playerManager.seekToLiveEdge() } - fun showControls() { + fun showControls(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) { _controlsVisible.value = true - scheduleAutoHide() + scheduleAutoHide(autoHideDelayMs) } fun toggleControlsVisibility() { _controlsVisible.value = !_controlsVisible.value - if (_controlsVisible.value) scheduleAutoHide() + if (_controlsVisible.value) scheduleAutoHide(DEFAULT_CONTROLS_AUTO_HIDE_MS) } - private fun scheduleAutoHide() { + private fun scheduleAutoHide(autoHideDelayMs: Long) { autoHideJob?.cancel() if (!player.isPlaying) return autoHideJob = viewModelScope.launch { - delay(3500) + delay(autoHideDelayMs) _controlsVisible.value = false } } - fun next() { + fun next(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) { playerManager.next() - showControls() + showControls(autoHideDelayMs) } - fun previous() { + fun previous(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) { playerManager.previous() - showControls() + showControls(autoHideDelayMs) } fun selectTrack(option: TrackOption) { @@ -235,9 +238,9 @@ class PlayerViewModel @Inject constructor( playerManager.retry() } - fun playQueueItem(id: String) { + fun playQueueItem(id: String, autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) { playerManager.playQueueItem(id) - showControls() + showControls(autoHideDelayMs) } fun clearError() {