diff options
| author | 2024-06-12 09:05:42 +0000 | |
|---|---|---|
| committer | 2024-06-12 09:05:42 +0000 | |
| commit | c06fafda3ff64b6a0c6d819f57c6a58bef1de64c (patch) | |
| tree | 2c812894d8942c55d76ccdbf89a8043be7125593 | |
| parent | ee4a566796d049eb526feaa7a54ab1d8636fef63 (diff) | |
| parent | 64ad8be75826c83bc1b488e9ea269e2af5591d8d (diff) | |
Merge "Don't interpolate animated values when overscrolling" into main
2 files changed, 64 insertions, 10 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 7fd3a176acaa..114dcf4fbc7e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -381,18 +381,17 @@ private class AnimatedStateImpl<T, Delta>( // relayout/redraw for nothing. fromValue } else { - // In the case of bouncing, if the value remains constant during the overscroll, we - // should use the value of the scene we are bouncing around. - if (!canOverflow && transition is TransitionState.HasOverscrollProperties) { - val bouncingScene = transition.bouncingScene - if (bouncingScene != null) { - return sharedValue[bouncingScene] + val overscrollSpec = transition.currentOverscrollSpec + val progress = + when { + overscrollSpec == null -> { + if (canOverflow) transition.progress + else transition.progress.fastCoerceIn(0f, 1f) + } + overscrollSpec.scene == transition.toScene -> 1f + else -> 0f } - } - val progress = - if (canOverflow) transition.progress - else transition.progress.fastCoerceIn(0f, 1f) sharedValue.type.lerp(fromValue, toValue, progress) } } else fromValue ?: toValue diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt index 6e8b208ea9e8..a7889e2fac58 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt @@ -18,10 +18,13 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -443,4 +446,56 @@ class AnimatedSharedAsStateTest { assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f) assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f) } + + @Test + fun animatedValueDoesNotOverscrollWhenOverscrollIsSpecified() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneB, Orientation.Horizontal) } + ) + } + + val key = ValueKey("foo") + val lastValues = mutableMapOf<SceneKey, Float>() + + @Composable + fun SceneScope.animateFloat(value: Float, key: ValueKey) { + val animatedValue = animateSceneFloatAsState(value, key) + LaunchedEffect(animatedValue) { + snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it } + } + } + + rule.setContent { + SceneTransitionLayout(state) { + scene(SceneA) { animateFloat(0f, key) } + scene(SceneB) { animateFloat(100f, key) } + } + } + + // Overscroll on A at -100%: value should be interpolated given that there is no overscroll + // defined for scene A. + var progress by mutableStateOf(-1f) + rule.runOnIdle { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(-100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(-100f) + + // Middle of the transition. + progress = 0.5f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(50f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(50f) + + // Overscroll on B at 200%: value should not be interpolated given that there is an + // overscroll defined for scene B. + progress = 2f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f) + } } |