diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt index d7c43c9916..807487d614 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/PlayerScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.viewinterop.AndroidView @@ -107,13 +108,24 @@ fun PlayerScreen( mutableLongStateOf(0L) } var isPlaying by remember { - mutableStateOf(viewModel.player.isPlaying) + mutableStateOf(true) + } + val onPause = { + viewModel.player.pause() + isPlaying = false + } + val onPlayPauseToggle = { + if (isPlaying) { + onPause() + } else { + viewModel.player.play() + isPlaying = true + } } LaunchedEffect(Unit) { while (true) { delay(300) currentPosition = viewModel.player.currentPosition - isPlaying = viewModel.player.isPlaying } } @@ -143,10 +155,16 @@ fun PlayerScreen( } } + var isFocused by remember { mutableStateOf(true) } Box( modifier = Modifier + .onFocusChanged { + isFocused = it.isFocused + } .dPadEvents( + isFocused = isFocused, exoPlayer = viewModel.player, + onPause = onPause, videoPlayerState = videoPlayerState, ) .focusable(), @@ -169,7 +187,6 @@ fun PlayerScreen( when (lifecycle) { Lifecycle.Event.ON_PAUSE -> { it.onPause() - it.player?.pause() } Lifecycle.Event.ON_RESUME -> { @@ -195,6 +212,7 @@ fun PlayerScreen( contentCurrentPosition = currentPosition, player = viewModel.player, state = videoPlayerState, + onPlayPauseToggle = onPlayPauseToggle, focusRequester = focusRequester, navigator = navigator, ) @@ -211,17 +229,10 @@ fun VideoPlayerControls( contentCurrentPosition: Long, player: Player, state: VideoPlayerState, + onPlayPauseToggle: () -> Unit, focusRequester: FocusRequester, navigator: DestinationsNavigator, ) { - val onPlayPauseToggle = { shouldPlay: Boolean -> - if (shouldPlay) { - player.play() - } else { - player.pause() - } - } - VideoPlayerControlsLayout( mediaTitle = { VideoPlayerMediaTitle( @@ -246,8 +257,6 @@ fun VideoPlayerControls( ) { VideoPlayerMediaButton( icon = painterResource(id = R.drawable.ic_speaker), - state = state, - isPlaying = isPlaying, onClick = { val tracks = getTracks(player, C.TRACK_TYPE_AUDIO) navigator.navigate(VideoPlayerTrackSelectorDialogDestination(C.TRACK_TYPE_AUDIO, tracks)) @@ -255,8 +264,6 @@ fun VideoPlayerControls( ) VideoPlayerMediaButton( icon = painterResource(id = R.drawable.ic_closed_caption), - state = state, - isPlaying = isPlaying, onClick = { val tracks = getTracks(player, C.TRACK_TYPE_TEXT) navigator.navigate(VideoPlayerTrackSelectorDialogDestination(C.TRACK_TYPE_TEXT, tracks)) @@ -268,18 +275,35 @@ fun VideoPlayerControls( } private fun Modifier.dPadEvents( + isFocused: Boolean, exoPlayer: Player, + onPause: () -> Unit, videoPlayerState: VideoPlayerState, -): Modifier = this.handleDPadKeyEvents( - onLeft = {}, - onRight = {}, - onUp = {}, - onDown = {}, - onEnter = { - exoPlayer.pause() - videoPlayerState.showControls() - }, -) +): Modifier = + this.handleDPadKeyEvents( + onLeft = { + if (isFocused) { + exoPlayer.seekBack() + } + }, + onRight = { + if (isFocused) { + exoPlayer.seekForward() + } + }, + onUp = { + if (isFocused) { + videoPlayerState.showControls() + } + }, + onDown = {}, + onEnter = { + if (isFocused) { + onPause() + videoPlayerState.showControls() + } + }, + ) @androidx.annotation.OptIn(UnstableApi::class) private fun getTracks(player: Player, type: Int): Array { diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerMediaButton.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerMediaButton.kt index 54e15b221c..7bebca9db5 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerMediaButton.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerMediaButton.kt @@ -1,10 +1,7 @@ package dev.jdtech.jellyfin.ui.components.player import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.graphics.painter.Painter import androidx.tv.material3.Icon @@ -13,18 +10,9 @@ import androidx.tv.material3.IconButton @Composable fun VideoPlayerMediaButton( icon: Painter, - state: VideoPlayerState, - isPlaying: Boolean, onClick: () -> Unit = {}, ) { val interactionSource = remember { MutableInteractionSource() } - val isFocused by interactionSource.collectIsFocusedAsState() - - LaunchedEffect(isFocused && isPlaying) { - if (isFocused && isPlaying) { - state.showControls() - } - } IconButton(onClick = onClick, interactionSource = interactionSource) { Icon(painter = icon, contentDescription = null) diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeekBar.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeekBar.kt index 43a79ab353..9e5a6c3943 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeekBar.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeekBar.kt @@ -33,6 +33,7 @@ import dev.jdtech.jellyfin.utils.handleDPadKeyEvents @Composable fun VideoPlayerSeekBar( progress: Float, + onPause: () -> Unit, onSeek: (seekProgress: Float) -> Unit, state: VideoPlayerState, ) { @@ -69,6 +70,7 @@ fun VideoPlayerSeekBar( onSeek(seekProgress) focusManager.moveFocus(FocusDirection.Exit) } else { + onPause() seekProgress = progress } isSelected = !isSelected @@ -125,6 +127,7 @@ fun VideoPlayerSeekBarPreview() { FindroidTheme { VideoPlayerSeekBar( progress = 0.4f, + onPause = {}, onSeek = {}, state = rememberVideoPlayerState(), ) diff --git a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeeker.kt b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeeker.kt index 4700403bf0..a4650bf1cd 100644 --- a/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeeker.kt +++ b/app/tv/src/main/java/dev/jdtech/jellyfin/ui/components/player/VideoPlayerSeeker.kt @@ -29,7 +29,7 @@ fun VideoPlayerSeeker( focusRequester: FocusRequester, state: VideoPlayerState, isPlaying: Boolean, - onPlayPauseToggle: (Boolean) -> Unit, + onPlayPauseToggle: () -> Unit, onSeek: (Float) -> Unit, contentProgress: Duration, contentDuration: Duration, @@ -51,12 +51,15 @@ fun VideoPlayerSeeker( } } + val onPause = + { if (isPlaying) onPlayPauseToggle() } + Row( verticalAlignment = Alignment.CenterVertically, ) { IconButton( onClick = { - onPlayPauseToggle(!isPlaying) + onPlayPauseToggle() }, modifier = Modifier.focusRequester(focusRequester), ) { @@ -92,6 +95,7 @@ fun VideoPlayerSeeker( Spacer(modifier = Modifier.height(MaterialTheme.spacings.small)) VideoPlayerSeekBar( progress = (contentProgress / contentDuration).toFloat(), + onPause = onPause, onSeek = onSeek, state = state, )