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

Commit c06fafda authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge "Don't interpolate animated values when overscrolling" into main

parents ee4a5667 64ad8be7
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)
    }
}