fix: adjust screen awake behaviour when playback error throws

fixes #877
This commit is contained in:
tapframe 2026-05-05 13:29:38 +05:30
parent f7e9778ca4
commit b941717dd9
5 changed files with 27 additions and 7 deletions

View file

@ -179,6 +179,10 @@ actual fun PlatformPlayerSurface(
var currentSubtitleStyle by remember { mutableStateOf(SubtitleStyleState.DEFAULT) } var currentSubtitleStyle by remember { mutableStateOf(SubtitleStyleState.DEFAULT) }
var subtitleSelectionJob by remember { mutableStateOf<Job?>(null) } var subtitleSelectionJob by remember { mutableStateOf<Job?>(null) }
fun syncPlayerViewKeepScreenOn() {
playerViewRef?.keepScreenOn = exoPlayer.shouldKeepPlayerScreenOn()
}
DisposableEffect(exoPlayer) { DisposableEffect(exoPlayer) {
PlayerPictureInPictureManager.registerPausePlaybackCallback { PlayerPictureInPictureManager.registerPausePlaybackCallback {
exoPlayer.pause() exoPlayer.pause()
@ -186,6 +190,7 @@ actual fun PlatformPlayerSurface(
val listener = object : Player.Listener { val listener = object : Player.Listener {
override fun onPlayerError(error: PlaybackException) { override fun onPlayerError(error: PlaybackException) {
syncPlayerViewKeepScreenOn()
latestOnError.value(error.localizedMessage ?: runBlocking { getString(Res.string.player_unable_to_play_stream) }) latestOnError.value(error.localizedMessage ?: runBlocking { getString(Res.string.player_unable_to_play_stream) })
} }
@ -202,10 +207,12 @@ actual fun PlatformPlayerSurface(
latestOnError.value(null) latestOnError.value(null)
exoPlayer.logCurrentTracks("STATE_READY") exoPlayer.logCurrentTracks("STATE_READY")
} }
syncPlayerViewKeepScreenOn()
latestOnSnapshot.value(exoPlayer.snapshot()) latestOnSnapshot.value(exoPlayer.snapshot())
} }
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
syncPlayerViewKeepScreenOn()
latestOnSnapshot.value(exoPlayer.snapshot()) latestOnSnapshot.value(exoPlayer.snapshot())
} }
@ -235,6 +242,7 @@ actual fun PlatformPlayerSurface(
onDispose { onDispose {
PlayerPictureInPictureManager.registerPausePlaybackCallback(null) PlayerPictureInPictureManager.registerPausePlaybackCallback(null)
exoPlayer.removeListener(listener) exoPlayer.removeListener(listener)
playerViewRef?.keepScreenOn = false
subtitleSelectionJob?.cancel() subtitleSelectionJob?.cancel()
} }
} }
@ -264,6 +272,7 @@ actual fun PlatformPlayerSurface(
LaunchedEffect(exoPlayer, playWhenReady) { LaunchedEffect(exoPlayer, playWhenReady) {
exoPlayer.playWhenReady = playWhenReady exoPlayer.playWhenReady = playWhenReady
syncPlayerViewKeepScreenOn()
latestOnSnapshot.value(exoPlayer.snapshot()) latestOnSnapshot.value(exoPlayer.snapshot())
} }
@ -424,7 +433,7 @@ actual fun PlatformPlayerSurface(
useController = useNativeController useController = useNativeController
layoutParams = android.view.ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) layoutParams = android.view.ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
player = exoPlayer player = exoPlayer
keepScreenOn = true keepScreenOn = exoPlayer.shouldKeepPlayerScreenOn()
this.resizeMode = resizeMode.toExoResizeMode() this.resizeMode = resizeMode.toExoResizeMode()
setShutterBackgroundColor(android.graphics.Color.BLACK) setShutterBackgroundColor(android.graphics.Color.BLACK)
playerViewRef = this playerViewRef = this
@ -441,6 +450,7 @@ actual fun PlatformPlayerSurface(
playerView.useController = useNativeController playerView.useController = useNativeController
playerView.resizeMode = resizeMode.toExoResizeMode() playerView.resizeMode = resizeMode.toExoResizeMode()
playerViewRef = playerView playerViewRef = playerView
syncPlayerViewKeepScreenOn()
playerView.syncLibassOverlay( playerView.syncLibassOverlay(
player = exoPlayer, player = exoPlayer,
enabled = useLibass, enabled = useLibass,
@ -469,6 +479,11 @@ private fun ExoPlayer.snapshot(): PlayerPlaybackSnapshot =
playbackSpeed = playbackParameters.speed, playbackSpeed = playbackParameters.speed,
) )
private fun ExoPlayer.shouldKeepPlayerScreenOn(): Boolean =
playerError == null &&
playWhenReady &&
playbackState in setOf(Player.STATE_BUFFERING, Player.STATE_READY)
private fun PlayerResizeMode.toExoResizeMode(): Int = private fun PlayerResizeMode.toExoResizeMode(): Int =
when (this) { when (this) {
PlayerResizeMode.Fit -> AspectRatioFrameLayout.RESIZE_MODE_FIT PlayerResizeMode.Fit -> AspectRatioFrameLayout.RESIZE_MODE_FIT

View file

@ -35,7 +35,7 @@ actual fun LockPlayerToLandscape() {
} }
@Composable @Composable
actual fun EnterImmersivePlayerMode() { actual fun EnterImmersivePlayerMode(keepScreenAwake: Boolean) {
val activity = LocalContext.current.findActivity() ?: return val activity = LocalContext.current.findActivity() ?: return
DisposableEffect(activity) { DisposableEffect(activity) {

View file

@ -19,7 +19,7 @@ data class PlayerAudioLevel(
expect fun LockPlayerToLandscape() expect fun LockPlayerToLandscape()
@Composable @Composable
expect fun EnterImmersivePlayerMode() expect fun EnterImmersivePlayerMode(keepScreenAwake: Boolean)
@Composable @Composable
expect fun ManagePlayerPictureInPicture( expect fun ManagePlayerPictureInPicture(

View file

@ -139,7 +139,6 @@ fun PlayerScreen(
initialProgressFraction: Float? = null, initialProgressFraction: Float? = null,
) { ) {
LockPlayerToLandscape() LockPlayerToLandscape()
EnterImmersivePlayerMode()
val playerSettingsUiState by remember { val playerSettingsUiState by remember {
PlayerSettingsRepository.ensureLoaded() PlayerSettingsRepository.ensureLoaded()
PlayerSettingsRepository.uiState PlayerSettingsRepository.uiState
@ -195,6 +194,9 @@ fun PlayerScreen(
var playerController by remember { mutableStateOf<PlayerEngineController?>(null) } var playerController by remember { mutableStateOf<PlayerEngineController?>(null) }
var playerControllerSourceUrl by remember { mutableStateOf<String?>(null) } var playerControllerSourceUrl by remember { mutableStateOf<String?>(null) }
var errorMessage by remember { mutableStateOf<String?>(null) } var errorMessage by remember { mutableStateOf<String?>(null) }
val keepScreenAwake = errorMessage == null &&
(playbackSnapshot.isPlaying || (shouldPlay && playbackSnapshot.isLoading))
EnterImmersivePlayerMode(keepScreenAwake = keepScreenAwake)
var scrubbingPositionMs by remember { mutableStateOf<Long?>(null) } var scrubbingPositionMs by remember { mutableStateOf<Long?>(null) }
var pausedOverlayVisible by remember { mutableStateOf(false) } var pausedOverlayVisible by remember { mutableStateOf(false) }
var gestureFeedback by remember { mutableStateOf<GestureFeedbackState?>(null) } var gestureFeedback by remember { mutableStateOf<GestureFeedbackState?>(null) }

View file

@ -2,6 +2,7 @@ package com.nuvio.app.features.player
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import platform.Foundation.NSNotificationCenter import platform.Foundation.NSNotificationCenter
@ -32,10 +33,12 @@ actual fun LockPlayerToLandscape() {
} }
@Composable @Composable
actual fun EnterImmersivePlayerMode() { actual fun EnterImmersivePlayerMode(keepScreenAwake: Boolean) {
SideEffect {
UIApplication.sharedApplication.setIdleTimerDisabled(keepScreenAwake)
}
DisposableEffect(Unit) { DisposableEffect(Unit) {
// Request idle timer disabled to keep screen awake during playback
UIApplication.sharedApplication.setIdleTimerDisabled(true)
onDispose { onDispose {
UIApplication.sharedApplication.setIdleTimerDisabled(false) UIApplication.sharedApplication.setIdleTimerDisabled(false)
} }