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

Commit f4898110 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Animate SceneTransitionLayout size

This CL animates the size of a SceneTransitionLayout when transitioning
between two scenes that don't have the same size.

Bug: 308961608
Test: atest SceneTransitionLayoutTest
Flag: NA
Change-Id: I668f1a1ebedd31be4460816fc596449a55c94a93
parent c5af4e14
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -457,7 +457,10 @@ private inline fun <T> computeValue(

    // There is no ongoing transition.
    if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
        return idleValue
        // Even if this element SceneTransitionLayout is not animated, the layout itself might be
        // animated (e.g. by another parent SceneTransitionLayout), in which case this element still
        // need to participate in the layout phase.
        return currentValue()
    }

    // A transition was started but it's not ready yet (not all elements have been composed/laid
+2 −1
Original line number Diff line number Diff line
@@ -2,9 +2,10 @@ package com.android.compose.animation.scene

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.unit.IntSize

interface DraggableHandler {
    fun onDragStarted(startedPosition: Offset, pointersDown: Int = 1)
    fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int = 1)
    fun onDelta(pixels: Float)
    fun onDragStopped(velocity: Float)
}
+3 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastForEach

@@ -60,7 +61,7 @@ internal fun Modifier.multiPointerDraggable(
    orientation: Orientation,
    enabled: Boolean,
    startDragImmediately: Boolean,
    onDragStarted: (startedPosition: Offset, pointersDown: Int) -> Unit,
    onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
    onDragDelta: (Float) -> Unit,
    onDragStopped: (velocity: Float) -> Unit,
): Modifier = composed {
@@ -83,7 +84,7 @@ internal fun Modifier.multiPointerDraggable(

        val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown ->
            velocityTracker.resetTracking()
            onDragStarted(startedPosition, pointersDown)
            onDragStarted(size, startedPosition, pointersDown)
        }

        val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) }
+14 −3
Original line number Diff line number Diff line
@@ -25,8 +25,9 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
@@ -44,14 +45,24 @@ internal class Scene(
    var content by mutableStateOf(content)
    var userActions by mutableStateOf(actions)
    var zIndex by mutableFloatStateOf(zIndex)
    var size by mutableStateOf(IntSize.Zero)
    var targetSize by mutableStateOf(IntSize.Zero)

    /** The shared values in this scene that are not tied to a specific element. */
    val sharedValues = SnapshotStateMap<ValueKey, Element.SharedValue<*>>()

    @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    fun Content(modifier: Modifier = Modifier) {
        Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) {
        Box(
            modifier
                .zIndex(zIndex)
                .intermediateLayout { measurable, constraints ->
                    targetSize = lookaheadSize
                    val placeable = measurable.measure(constraints)
                    layout(placeable.width, placeable.height) { placeable.place(0, 0) }
                }
                .testTag(key.testTag)
        ) {
            scope.content()
        }
    }
+12 −7
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
@@ -90,7 +91,7 @@ class SceneGestureHandler(

    internal var gestureWithPriority: Any? = null

    internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) {
    internal fun onDragStarted(pointersDown: Int, layoutSize: IntSize, startedPosition: Offset?) {
        if (isDrivingTransition) {
            // This [transition] was already driving the animation: simply take over it.
            // Stop animating and start from where the current offset.
@@ -126,14 +127,14 @@ class SceneGestureHandler(
        // we will also have to make sure that we correctly handle overscroll.
        swipeTransition.absoluteDistance =
            when (orientation) {
                Orientation.Horizontal -> layoutImpl.size.width
                Orientation.Vertical -> layoutImpl.size.height
                Orientation.Horizontal -> layoutSize.width
                Orientation.Vertical -> layoutSize.height
            }.toFloat()

        val fromEdge =
            startedPosition?.let { position ->
                layoutImpl.edgeDetector.edge(
                    layoutImpl.size,
                    layoutSize,
                    position.round(),
                    layoutImpl.density,
                    orientation,
@@ -513,9 +514,9 @@ class SceneGestureHandler(
private class SceneDraggableHandler(
    private val gestureHandler: SceneGestureHandler,
) : DraggableHandler {
    override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
    override fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) {
        gestureHandler.gestureWithPriority = this
        gestureHandler.onDragStarted(pointersDown, startedPosition)
        gestureHandler.onDragStarted(pointersDown, layoutSize, startedPosition)
    }

    override fun onDelta(pixels: Float) {
@@ -647,7 +648,11 @@ class SceneNestedScrollHandler(
            canContinueScroll = { true },
            onStart = {
                gestureHandler.gestureWithPriority = this
                gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null)
                gestureHandler.onDragStarted(
                    pointersDown = 1,
                    layoutSize = gestureHandler.currentScene.targetSize,
                    startedPosition = null,
                )
            },
            onScroll = { offsetAvailable ->
                if (gestureHandler.gestureWithPriority != this) {
Loading