Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64ad8be7 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Don't interpolate animated values when overscrolling

This CL ensures that we don't interpolate animated shared values when
there is an overscroll defined on the scene that is overscrolling.

Bug: 291053278
Test: atest SceneTransitionLayoutTest
Flag: com.android.systemui.scene_container
Change-Id: I6bf1f9fa6ed1d4e16cc7ba0e511c21dddb094533
parent 28c272fe
Loading
Loading
Loading
Loading
+9 −10
Original line number Diff line number Diff line
@@ -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
                    }

                sharedValue.type.lerp(fromValue, toValue, progress)
            }
        } else fromValue ?: toValue
+55 −0
Original line number Diff line number Diff line
@@ -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)
    }
}