From 0de70e51cd2014d8065ce2af79e1a5dd960bdcb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 02:27:05 +0000 Subject: [PATCH] fix(video): pause playback when app goes to background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-introduces the lifecycle observer that was dropped in the Feb 24 flow refactor of GetVideoController (f2410a69 "removing most of the little hacks to get the controller to work in the lifecycle"). Without it, the scroll-position mutex is the only thing that pauses a video — and the window's visible rect doesn't change on ON_PAUSE, so the currently-playing video kept playing forever behind other apps. Pause on ON_PAUSE, resume on ON_RESUME if the video is still the visible/active one. The explicit BackgroundMedia (PiP) instance is preserved as the opt-in to keep playing. --- .../composable/ControlWhenPlayerIsActive.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/ControlWhenPlayerIsActive.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/ControlWhenPlayerIsActive.kt index 8d2be3a9c..a26dfbdb6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/ControlWhenPlayerIsActive.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/ControlWhenPlayerIsActive.kt @@ -26,6 +26,9 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.ui.platform.LocalView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.media3.common.Player import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia @@ -75,6 +78,44 @@ fun ControlWhenPlayerIsActive( listener.destroy() } } + + // Pause when the host activity leaves the foreground; resume the visible + // video when it comes back. The scroll-based mutex above only reacts to + // on-screen position, which doesn't change on background — so without + // this, videos play forever behind another app. + // Skip the explicit BackgroundMedia (PiP) instance: the user opted that + // one into keep-playing. + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner, mediaControllerState) { + val observer = + LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_PAUSE -> { + if (controller.isPlaying && !BackgroundMedia.isMutex(mediaControllerState)) { + controller.pause() + } + } + + Lifecycle.Event.ON_RESUME -> { + if (automaticallyStartPlayback && + isClosestToTheCenterOfTheScreen.value && + !controller.isPlaying + ) { + if (BackgroundMedia.hasBackgroundButNot(mediaControllerState)) { + controller.volume = 0f + } + controller.play() + } + } + + else -> { + Unit + } + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } + } } class PlayerEventListener(