From 68b5d1385689bd4b2f619f73be9f0191c95c39d4 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Martinez Date: Tue, 8 Sep 2026 08:57:10 -0700 Subject: [PATCH] Making LavaBeats shader time relative to the STARTED lifecycle state This makes sure that the shader time is always relative to the lifecycle of the app, avoiding the storage of very large numbers over time as the device is left on for a long period of time. The latter makes the effect laggy and janky due to computations with large numbers --- .../ui/haptics/lavabeats/LavaBeatsGraphics.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/samples/user-interface/haptics/src/main/java/com/example/platform/ui/haptics/lavabeats/LavaBeatsGraphics.kt b/samples/user-interface/haptics/src/main/java/com/example/platform/ui/haptics/lavabeats/LavaBeatsGraphics.kt index 1f899a27..8f80bc33 100644 --- a/samples/user-interface/haptics/src/main/java/com/example/platform/ui/haptics/lavabeats/LavaBeatsGraphics.kt +++ b/samples/user-interface/haptics/src/main/java/com/example/platform/ui/haptics/lavabeats/LavaBeatsGraphics.kt @@ -27,12 +27,16 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.graphics.ShaderBrush import androidx.compose.ui.platform.LocalDensity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle @Composable fun LavaBeatsGraphics( @@ -47,21 +51,22 @@ fun LavaBeatsGraphics( val density = LocalDensity.current val width = with(density) { constraints.maxWidth.toPx() } val height = with(density) { constraints.maxHeight.toPx() } - var firstTime by remember { mutableFloatStateOf(-1f) } + var startTimeMillis by remember { mutableLongStateOf(-1L) } var time by remember { mutableFloatStateOf(0f) } + + val lifecycleOwner = LocalLifecycleOwner.current val isInDarkMode = isSystemInDarkTheme() val surfaceColor = MaterialTheme.colorScheme.background - LaunchedEffect(Unit) { - // Use withInfiniteAnimationFrameMillis to update the time uniform per frame. - // This is a more efficient approach than passing a new shader instance - // or re-creating the RenderEffect on every frame. - while (true) { - withInfiniteAnimationFrameMillis { frameTime -> - if (firstTime == -1f) { - firstTime = frameTime / 1000f - } else { - time = frameTime / 1000f - firstTime + LaunchedEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + startTimeMillis = -1L + while (true) { + withInfiniteAnimationFrameMillis { frameTime -> + if (startTimeMillis == -1L) { + startTimeMillis = frameTime + } + time = (frameTime - startTimeMillis) / 1000f } } }