mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
feat: Implement auto-hide delay for TV player controls
This commit is contained in:
@@ -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.TvTrackPanelType
|
||||||
import hu.bbara.purefin.tv.player.components.TvTrackSelectionPanel
|
import hu.bbara.purefin.tv.player.components.TvTrackSelectionPanel
|
||||||
|
|
||||||
|
private const val TV_CONTROLS_AUTO_HIDE_MS = 5_000L
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TvPlayerScreen(
|
fun TvPlayerScreen(
|
||||||
@@ -82,12 +84,36 @@ fun TvPlayerScreen(
|
|||||||
|
|
||||||
LaunchedEffect(uiState.isPlaying) {
|
LaunchedEffect(uiState.isPlaying) {
|
||||||
if (uiState.isPlaying && controlsVisible) {
|
if (uiState.isPlaying && controlsVisible) {
|
||||||
viewModel.toggleControlsVisibility()
|
viewModel.showControls(TV_CONTROLS_AUTO_HIDE_MS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val hiddenControlFocusRequester = remember { FocusRequester() }
|
val hiddenControlFocusRequester = remember { FocusRequester() }
|
||||||
val controlsFocusRequester = 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) {
|
LaunchedEffect(controlsVisible) {
|
||||||
if (controlsVisible) {
|
if (controlsVisible) {
|
||||||
@@ -106,23 +132,22 @@ fun TvPlayerScreen(
|
|||||||
if (!controlsVisible && event.type == KeyEventType.KeyDown) {
|
if (!controlsVisible && event.type == KeyEventType.KeyDown) {
|
||||||
when (event.key) {
|
when (event.key) {
|
||||||
Key.DirectionLeft -> {
|
Key.DirectionLeft -> {
|
||||||
viewModel.seekBy(-10_000)
|
seekByAndShowControls(-10_000)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.DirectionRight -> {
|
Key.DirectionRight -> {
|
||||||
viewModel.seekBy(10_000)
|
seekByAndShowControls(10_000)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.DirectionUp, Key.DirectionDown -> {
|
Key.DirectionUp, Key.DirectionDown -> {
|
||||||
viewModel.showControls()
|
showTvControls()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.DirectionCenter, Key.Enter -> {
|
Key.DirectionCenter, Key.Enter -> {
|
||||||
viewModel.togglePlayPause()
|
togglePlayPauseAndShowControls()
|
||||||
viewModel.showControls()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,12 +182,12 @@ fun TvPlayerScreen(
|
|||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
focusRequester = controlsFocusRequester,
|
focusRequester = controlsFocusRequester,
|
||||||
onPlayPause = { viewModel.togglePlayPause() },
|
onPlayPause = togglePlayPauseAndShowControls,
|
||||||
onSeek = { viewModel.seekTo(it) },
|
onSeek = seekAndShowControls,
|
||||||
onSeekRelative = { viewModel.seekBy(it) },
|
onSeekRelative = seekByAndShowControls,
|
||||||
onSeekLiveEdge = { viewModel.seekToLiveEdge() },
|
onSeekLiveEdge = seekToLiveEdgeAndShowControls,
|
||||||
onNext = { viewModel.next() },
|
onNext = nextAndShowControls,
|
||||||
onPrevious = { viewModel.previous() },
|
onPrevious = previousAndShowControls,
|
||||||
onOpenAudioPanel = { trackPanelType = TvTrackPanelType.AUDIO },
|
onOpenAudioPanel = { trackPanelType = TvTrackPanelType.AUDIO },
|
||||||
onOpenSubtitlesPanel = { trackPanelType = TvTrackPanelType.SUBTITLES },
|
onOpenSubtitlesPanel = { trackPanelType = TvTrackPanelType.SUBTITLES },
|
||||||
onOpenQualityPanel = { trackPanelType = TvTrackPanelType.QUALITY },
|
onOpenQualityPanel = { trackPanelType = TvTrackPanelType.QUALITY },
|
||||||
@@ -174,10 +199,10 @@ fun TvPlayerScreen(
|
|||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onRetry = { viewModel.retry() },
|
onRetry = { viewModel.retry() },
|
||||||
onNext = { viewModel.next() },
|
onNext = nextAndShowControls,
|
||||||
onReplay = {
|
onReplay = {
|
||||||
viewModel.seekTo(0L)
|
viewModel.seekTo(0L)
|
||||||
viewModel.togglePlayPause()
|
togglePlayPauseAndShowControls()
|
||||||
},
|
},
|
||||||
onDismissError = { viewModel.clearError() }
|
onDismissError = { viewModel.clearError() }
|
||||||
)
|
)
|
||||||
@@ -209,7 +234,7 @@ fun TvPlayerScreen(
|
|||||||
TvPlayerQueuePanel(
|
TvPlayerQueuePanel(
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onSelect = { id ->
|
onSelect = { id ->
|
||||||
viewModel.playQueueItem(id)
|
viewModel.playQueueItem(id, TV_CONTROLS_AUTO_HIDE_MS)
|
||||||
showQueuePanel = false
|
showQueuePanel = false
|
||||||
},
|
},
|
||||||
onClose = { showQueuePanel = false },
|
onClose = { showQueuePanel = false },
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ class PlayerViewModel @Inject constructor(
|
|||||||
private val mediaRepository: MediaRepository,
|
private val mediaRepository: MediaRepository,
|
||||||
private val progressManager: ProgressManager
|
private val progressManager: ProgressManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
companion object {
|
||||||
|
private const val DEFAULT_CONTROLS_AUTO_HIDE_MS = 3_500L
|
||||||
|
}
|
||||||
|
|
||||||
val player get() = playerManager.player
|
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()
|
playerManager.togglePlayPause()
|
||||||
showControls()
|
showControls(autoHideDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekTo(positionMs: Long) {
|
fun seekTo(positionMs: Long) {
|
||||||
@@ -193,33 +196,33 @@ class PlayerViewModel @Inject constructor(
|
|||||||
playerManager.seekToLiveEdge()
|
playerManager.seekToLiveEdge()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showControls() {
|
fun showControls(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) {
|
||||||
_controlsVisible.value = true
|
_controlsVisible.value = true
|
||||||
scheduleAutoHide()
|
scheduleAutoHide(autoHideDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleControlsVisibility() {
|
fun toggleControlsVisibility() {
|
||||||
_controlsVisible.value = !_controlsVisible.value
|
_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()
|
autoHideJob?.cancel()
|
||||||
if (!player.isPlaying) return
|
if (!player.isPlaying) return
|
||||||
autoHideJob = viewModelScope.launch {
|
autoHideJob = viewModelScope.launch {
|
||||||
delay(3500)
|
delay(autoHideDelayMs)
|
||||||
_controlsVisible.value = false
|
_controlsVisible.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun next() {
|
fun next(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) {
|
||||||
playerManager.next()
|
playerManager.next()
|
||||||
showControls()
|
showControls(autoHideDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun previous() {
|
fun previous(autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) {
|
||||||
playerManager.previous()
|
playerManager.previous()
|
||||||
showControls()
|
showControls(autoHideDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectTrack(option: TrackOption) {
|
fun selectTrack(option: TrackOption) {
|
||||||
@@ -235,9 +238,9 @@ class PlayerViewModel @Inject constructor(
|
|||||||
playerManager.retry()
|
playerManager.retry()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playQueueItem(id: String) {
|
fun playQueueItem(id: String, autoHideDelayMs: Long = DEFAULT_CONTROLS_AUTO_HIDE_MS) {
|
||||||
playerManager.playQueueItem(id)
|
playerManager.playQueueItem(id)
|
||||||
showControls()
|
showControls(autoHideDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearError() {
|
fun clearError() {
|
||||||
|
|||||||
Reference in New Issue
Block a user