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

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

Remove the dependency of SwipeAnimation on STLImpl

This CL removes the dependency that SwipeAnimation has on
SceneTransitionLayoutImpl. This will allow users of STL to easily create
their own SwipeAnimation to drive an STLState.

Bug: 362727477
Test: atest DraggableHandlerTest
Flag: com.android.systemui.scene_container
Change-Id: I2f7486442dc257674a7b7781b35938c7b1fd4bf0
parent 250ece66
Loading
Loading
Loading
Loading
+34 −33
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ internal class DraggableHandlerImpl(
        // Only intercept the current transition if one of the 2 swipes results is also a transition
        // between the same pair of contents.
        val swipes = computeSwipes(startedPosition, pointersDown = 1)
        val fromContent = swipeAnimation.currentContent
        val fromContent = layoutImpl.content(swipeAnimation.currentContent)
        val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent)
        val currentScene = layoutImpl.state.currentScene
        val contentTransition = swipeAnimation.contentTransition
@@ -145,7 +145,9 @@ internal class DraggableHandlerImpl(
            // We need to recompute the swipe results since this is a new gesture, and the
            // fromScene.userActions may have changed.
            val swipes = oldDragController.swipes
            swipes.updateSwipesResults(fromContent = oldSwipeAnimation.fromContent)
            swipes.updateSwipesResults(
                fromContent = layoutImpl.content(oldSwipeAnimation.fromContent)
            )

            // A new gesture should always create a new SwipeAnimation. This way there cannot be
            // different gestures controlling the same transition.
@@ -155,8 +157,10 @@ internal class DraggableHandlerImpl(

        val swipes = computeSwipes(startedPosition, pointersDown)
        val fromContent = layoutImpl.contentForUserActions()

        swipes.updateSwipesResults(fromContent)
        val result =
            swipes.findUserActionResult(fromContent, overSlop, updateSwipesResults = true)
            swipes.findUserActionResult(overSlop)
                // As we were unable to locate a valid target scene, the initial SwipeAnimation
                // cannot be defined. Consequently, a simple NoOp Controller will be returned.
                ?: return NoOpDragController
@@ -188,7 +192,13 @@ internal class DraggableHandlerImpl(
                else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
            }

        return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
        return createSwipeAnimation(
            layoutImpl,
            layoutImpl.coroutineScope,
            result,
            isUpOrLeft,
            orientation
        )
    }

    private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes {
@@ -291,7 +301,7 @@ private class DragControllerImpl(
        return onDrag(delta, swipeAnimation)
    }

    private fun <T : Content> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
    private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
        if (delta == 0f || !isDrivingTransition || swipeAnimation.isFinishing) {
            return 0f
        }
@@ -304,12 +314,12 @@ private class DragControllerImpl(
        fun hasReachedToSceneUpOrLeft() =
            distance < 0 &&
                desiredOffset <= distance &&
                swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent.key
                swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent

        fun hasReachedToSceneDownOrRight() =
            distance > 0 &&
                desiredOffset >= distance &&
                swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent.key
                swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent

        // Considering accelerated swipe: Change fromContent in the case where the user quickly
        // swiped multiple times in the same direction to accelerate the transition from A => B then
@@ -321,7 +331,7 @@ private class DragControllerImpl(
            swipeAnimation.currentContent == toContent &&
                (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight())

        val fromContent: Content
        val fromContent: ContentKey
        val currentTransitionOffset: Float
        val newOffset: Float
        val consumedDelta: Float
@@ -357,12 +367,10 @@ private class DragControllerImpl(

        swipeAnimation.dragOffset = currentTransitionOffset

        val result =
            swipes.findUserActionResult(
                fromContent = fromContent,
                directionOffset = newOffset,
                updateSwipesResults = hasReachedToContent
            )
        if (hasReachedToContent) {
            swipes.updateSwipesResults(draggableHandler.layoutImpl.content(fromContent))
        }
        val result = swipes.findUserActionResult(directionOffset = newOffset)

        if (result == null) {
            onStop(velocity = delta, canChangeContent = true)
@@ -371,7 +379,7 @@ private class DragControllerImpl(

        val needNewTransition =
            hasReachedToContent ||
                result.toContent(layoutState.currentScene) != swipeAnimation.toContent.key ||
                result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
                result.transitionKey != swipeAnimation.contentTransition.key

        if (needNewTransition) {
@@ -390,7 +398,7 @@ private class DragControllerImpl(
        return onStop(velocity, canChangeContent, swipeAnimation)
    }

    private fun <T : Content> onStop(
    private fun <T : ContentKey> onStop(
        velocity: Float,
        canChangeContent: Boolean,

@@ -407,7 +415,6 @@ private class DragControllerImpl(

        fun animateTo(targetContent: T) {
            swipeAnimation.animateOffset(
                coroutineScope = draggableHandler.coroutineScope,
                initialVelocity = velocity,
                targetContent = targetContent,
            )
@@ -518,6 +525,14 @@ internal class Swipes(
        return upOrLeftResult to downOrRightResult
    }

    /**
     * Update the swipes results.
     *
     * Usually we don't want to update them while doing a drag, because this could change the target
     * content (jump cutting) to a different content, when some system state changed the targets the
     * background. However, an update is needed any time we calculate the targets for a new
     * fromContent.
     */
    fun updateSwipesResults(fromContent: Content) {
        val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromContent)

@@ -526,31 +541,17 @@ internal class Swipes(
    }

    /**
     * Returns the [UserActionResult] from [fromContent] in the direction of [directionOffset].
     * Returns the [UserActionResult] in the direction of [directionOffset].
     *
     * @param fromContent the content from which we look for the target
     * @param directionOffset signed float that indicates the direction. Positive is down or right
     *   negative is up or left.
     * @param updateSwipesResults whether the swipe results should be updated to the current values
     *   held in the user actions map. Usually we don't want to update them while doing a drag,
     *   because this could change the target content (jump cutting) to a different content, when
     *   some system state changed the targets the background. However, an update is needed any time
     *   we calculate the targets for a new fromContent.
     * @return null when there are no targets in either direction. If one direction is null and you
     *   drag into the null direction this function will return the opposite direction, assuming
     *   that the users intention is to start the drag into the other direction eventually. If
     *   [directionOffset] is 0f and both direction are available, it will default to
     *   [upOrLeftResult].
     */
    fun findUserActionResult(
        fromContent: Content,
        directionOffset: Float,
        updateSwipesResults: Boolean,
    ): UserActionResult? {
        if (updateSwipesResults) {
            updateSwipesResults(fromContent)
        }

    fun findUserActionResult(directionOffset: Float): UserActionResult? {
        return when {
            upOrLeftResult == null && downOrRightResult == null -> null
            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
+2 −3
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import com.android.compose.animation.scene.content.Content
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
@@ -43,6 +42,7 @@ internal fun PredictiveBackHandler(
        val animation =
            createSwipeAnimation(
                layoutImpl,
                layoutImpl.coroutineScope,
                result,
                isUpOrLeft = false,
                // Note that the orientation does not matter here given that it's only used to
@@ -55,7 +55,7 @@ internal fun PredictiveBackHandler(
    }
}

private suspend fun <T : Content> animate(
private suspend fun <T : ContentKey> animate(
    layoutImpl: SceneTransitionLayoutImpl,
    animation: SwipeAnimation<T>,
    progress: Flow<BackEventCompat>,
@@ -68,7 +68,6 @@ private suspend fun <T : Content> animate(
        }

        animation.animateOffset(
            layoutImpl.coroutineScope,
            initialVelocity = 0f,
            targetContent = targetContent,

+126 −95
Original line number Diff line number Diff line
@@ -25,9 +25,6 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.Overlay
import com.android.compose.animation.scene.content.Scene
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import kotlin.math.absoluteValue
@@ -36,31 +33,96 @@ import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

internal fun createSwipeAnimation(
    layoutState: MutableSceneTransitionLayoutStateImpl,
    animationScope: CoroutineScope,
    result: UserActionResult,
    isUpOrLeft: Boolean,
    orientation: Orientation,
    distance: Float,
): SwipeAnimation<*> {
    return createSwipeAnimation(
        layoutState,
        animationScope,
        result,
        isUpOrLeft,
        orientation,
        distance = { distance },
        contentForUserActions = {
            error("Computing contentForUserActions requires a SceneTransitionLayoutImpl")
        },
    )
}

internal fun createSwipeAnimation(
    layoutImpl: SceneTransitionLayoutImpl,
    animationScope: CoroutineScope,
    result: UserActionResult,
    isUpOrLeft: Boolean,
    orientation: Orientation,
    distance: Float = DistanceUnspecified
): SwipeAnimation<*> {
    var lastDistance = distance

    fun distance(animation: SwipeAnimation<*>): Float {
        if (lastDistance != DistanceUnspecified) {
            return lastDistance
        }

        val absoluteDistance =
            with(animation.contentTransition.transformationSpec.distance ?: DefaultSwipeDistance) {
                layoutImpl.userActionDistanceScope.absoluteDistance(
                    layoutImpl.content(animation.fromContent).targetSize,
                    orientation,
                )
            }

        if (absoluteDistance <= 0f) {
            return DistanceUnspecified
        }

        val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
        lastDistance = distance
        return distance
    }

    return createSwipeAnimation(
        layoutImpl.state,
        animationScope,
        result,
        isUpOrLeft,
        orientation,
        distance = ::distance,
        contentForUserActions = { layoutImpl.contentForUserActions().key },
    )
}

private fun createSwipeAnimation(
    layoutState: MutableSceneTransitionLayoutStateImpl,
    animationScope: CoroutineScope,
    result: UserActionResult,
    isUpOrLeft: Boolean,
    orientation: Orientation,
    distance: Float = DistanceUnspecified,
    distance: (SwipeAnimation<*>) -> Float,
    contentForUserActions: () -> ContentKey,
): SwipeAnimation<*> {
    fun <T : Content> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
    fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
        return SwipeAnimation(
            layoutImpl = layoutImpl,
            layoutState = layoutState,
            animationScope = animationScope,
            fromContent = fromContent,
            toContent = toContent,
            userActionDistanceScope = layoutImpl.userActionDistanceScope,
            orientation = orientation,
            isUpOrLeft = isUpOrLeft,
            requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
            lastDistance = distance,
            distance = distance,
        )
    }

    val layoutState = layoutImpl.state
    return when (result) {
        is UserActionResult.ChangeScene -> {
            val fromScene = layoutImpl.scene(layoutState.currentScene)
            val toScene = layoutImpl.scene(result.toScene)
            val fromScene = layoutState.currentScene
            val toScene = result.toScene
            ChangeSceneSwipeTransition(
                    layoutState = layoutState,
                    swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = toScene),
@@ -70,12 +132,12 @@ internal fun createSwipeAnimation(
                .swipeAnimation
        }
        is UserActionResult.ShowOverlay -> {
            val fromScene = layoutImpl.scene(layoutState.currentScene)
            val overlay = layoutImpl.overlay(result.overlay)
            val fromScene = layoutState.currentScene
            val overlay = result.overlay
            ShowOrHideOverlaySwipeTransition(
                    layoutState = layoutState,
                    _fromOrToScene = fromScene,
                    _overlay = overlay,
                    fromOrToScene = fromScene,
                    overlay = overlay,
                    swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = overlay),
                    key = result.transitionKey,
                    replacedTransition = null,
@@ -83,12 +145,12 @@ internal fun createSwipeAnimation(
                .swipeAnimation
        }
        is UserActionResult.HideOverlay -> {
            val toScene = layoutImpl.scene(layoutState.currentScene)
            val overlay = layoutImpl.overlay(result.overlay)
            val toScene = layoutState.currentScene
            val overlay = result.overlay
            ShowOrHideOverlaySwipeTransition(
                    layoutState = layoutState,
                    _fromOrToScene = toScene,
                    _overlay = overlay,
                    fromOrToScene = toScene,
                    overlay = overlay,
                    swipeAnimation = swipeAnimation(fromContent = overlay, toContent = toScene),
                    key = result.transitionKey,
                    replacedTransition = null,
@@ -96,8 +158,14 @@ internal fun createSwipeAnimation(
                .swipeAnimation
        }
        is UserActionResult.ReplaceByOverlay -> {
            val fromOverlay = layoutImpl.contentForUserActions() as Overlay
            val toOverlay = layoutImpl.overlay(result.overlay)
            val fromOverlay =
                when (val contentForUserActions = contentForUserActions()) {
                    is SceneKey ->
                        error("ReplaceByOverlay can only be called when an overlay is shown")
                    is OverlayKey -> contentForUserActions
                }

            val toOverlay = result.overlay
            ReplaceOverlaySwipeTransition(
                    layoutState = layoutState,
                    swipeAnimation =
@@ -127,15 +195,15 @@ internal fun createSwipeAnimation(old: SwipeAnimation<*>): SwipeAnimation<*> {
}

/** A helper class that contains the main logic for swipe transitions. */
internal class SwipeAnimation<T : Content>(
    val layoutImpl: SceneTransitionLayoutImpl,
internal class SwipeAnimation<T : ContentKey>(
    val layoutState: MutableSceneTransitionLayoutStateImpl,
    val animationScope: CoroutineScope,
    val fromContent: T,
    val toContent: T,
    private val userActionDistanceScope: UserActionDistanceScope,
    override val orientation: Orientation,
    override val isUpOrLeft: Boolean,
    val requiresFullDistanceSwipe: Boolean,
    private var lastDistance: Float = DistanceUnspecified,
    private val distance: (SwipeAnimation<T>) -> Float,
    currentContent: T = fromContent,
    dragOffset: Float = 0f,
) : TransitionState.HasOverscrollProperties {
@@ -200,17 +268,8 @@ internal class SwipeAnimation<T : Content>(
    val isUserInputOngoing: Boolean
        get() = offsetAnimation == null

    override val overscrollScope: OverscrollScope =
        object : OverscrollScope {
            override val density: Float
                get() = layoutImpl.density.density

            override val fontScale: Float
                get() = layoutImpl.density.fontScale

    override val absoluteDistance: Float
        get() = distance().absoluteValue
        }

    /** Whether [finish] was called on this animation. */
    var isFinishing = false
@@ -219,14 +278,14 @@ internal class SwipeAnimation<T : Content>(
    constructor(
        other: SwipeAnimation<T>
    ) : this(
        layoutImpl = other.layoutImpl,
        layoutState = other.layoutState,
        animationScope = other.animationScope,
        fromContent = other.fromContent,
        toContent = other.toContent,
        userActionDistanceScope = other.userActionDistanceScope,
        orientation = other.orientation,
        isUpOrLeft = other.isUpOrLeft,
        requiresFullDistanceSwipe = other.requiresFullDistanceSwipe,
        lastDistance = other.lastDistance,
        distance = other.distance,
        currentContent = other.currentContent,
        dragOffset = other.dragOffset,
    )
@@ -239,27 +298,7 @@ internal class SwipeAnimation<T : Content>(
     * transition when the distance depends on the size or position of an element that is composed
     * in the content we are going to.
     */
    fun distance(): Float {
        if (lastDistance != DistanceUnspecified) {
            return lastDistance
        }

        val absoluteDistance =
            with(contentTransition.transformationSpec.distance ?: DefaultSwipeDistance) {
                userActionDistanceScope.absoluteDistance(
                    fromContent.targetSize,
                    orientation,
                )
            }

        if (absoluteDistance <= 0f) {
            return DistanceUnspecified
        }

        val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
        lastDistance = distance
        return distance
    }
    fun distance(): Float = distance(this)

    /** Ends any previous [offsetAnimation] and runs the new [animation]. */
    private fun startOffsetAnimation(animation: () -> OffsetAnimation): OffsetAnimation {
@@ -279,8 +318,6 @@ internal class SwipeAnimation<T : Content>(
    }

    fun animateOffset(
        // TODO(b/317063114) The CoroutineScope should be removed.
        coroutineScope: CoroutineScope,
        initialVelocity: Float,
        targetContent: T,
        spec: SpringSpec<Float>? = null,
@@ -329,7 +366,7 @@ internal class SwipeAnimation<T : Content>(
            val startedWhenOvercrollingTargetContent =
                if (targetContent == fromContent) initialProgress < 0f else initialProgress > 1f
            val job =
                coroutineScope
                animationScope
                    // Important: We start atomically to make sure that we start the coroutine even
                    // if it is cancelled right after it is launched, so that snapToContent() is
                    // correctly called. Otherwise, this transition will never be stopped and we
@@ -347,7 +384,7 @@ internal class SwipeAnimation<T : Content>(
                            val swipeSpec =
                                spec
                                    ?: contentTransition.transformationSpec.swipeSpec
                                    ?: layoutImpl.state.transitions.defaultSwipeSpec
                                    ?: layoutState.transitions.defaultSwipeSpec
                            animatable.animateTo(
                                targetValue = targetOffset,
                                animationSpec = swipeSpec,
@@ -370,7 +407,7 @@ internal class SwipeAnimation<T : Content>(
                                        }

                                    if (isBouncing) {
                                        bouncingContent = targetContent.key
                                        bouncingContent = targetContent

                                        // Immediately stop this transition if we are bouncing on a
                                        // content that does not bounce.
@@ -389,20 +426,19 @@ internal class SwipeAnimation<T : Content>(
        }
    }

    private fun canChangeContent(targetContent: Content): Boolean {
        val layoutState = layoutImpl.state
    private fun canChangeContent(targetContent: ContentKey): Boolean {
        return when (val transition = contentTransition) {
            is TransitionState.Transition.ChangeScene ->
                layoutState.canChangeScene(targetContent.key as SceneKey)
                layoutState.canChangeScene(targetContent as SceneKey)
            is TransitionState.Transition.ShowOrHideOverlay -> {
                if (targetContent.key == transition.overlay) {
                if (targetContent == transition.overlay) {
                    layoutState.canShowOverlay(transition.overlay)
                } else {
                    layoutState.canHideOverlay(transition.overlay)
                }
            }
            is TransitionState.Transition.ReplaceOverlay -> {
                val to = targetContent.key as OverlayKey
                val to = targetContent as OverlayKey
                val from =
                    if (to == transition.toOverlay) transition.fromOverlay else transition.toOverlay
                layoutState.canReplaceOverlay(from, to)
@@ -413,7 +449,7 @@ internal class SwipeAnimation<T : Content>(
    private fun snapToContent(content: T) {
        cancelOffsetAnimation()
        check(currentContent == content)
        layoutImpl.state.finishTransition(contentTransition)
        layoutState.finishTransition(contentTransition)
    }

    fun finish(): Job {
@@ -426,12 +462,7 @@ internal class SwipeAnimation<T : Content>(
        }

        // Animate to the current content.
        val animation =
            animateOffset(
                coroutineScope = layoutImpl.coroutineScope,
                initialVelocity = 0f,
                targetContent = currentContent,
            )
        val animation = animateOffset(initialVelocity = 0f, targetContent = currentContent)
        check(offsetAnimation == animation)
        return animation.job
    }
@@ -459,13 +490,13 @@ private object DefaultSwipeDistance : UserActionDistance {

private class ChangeSceneSwipeTransition(
    val layoutState: MutableSceneTransitionLayoutStateImpl,
    val swipeAnimation: SwipeAnimation<Scene>,
    val swipeAnimation: SwipeAnimation<SceneKey>,
    override val key: TransitionKey?,
    replacedTransition: ChangeSceneSwipeTransition?,
) :
    TransitionState.Transition.ChangeScene(
        swipeAnimation.fromContent.key,
        swipeAnimation.toContent.key,
        swipeAnimation.fromContent,
        swipeAnimation.toContent,
        replacedTransition,
    ),
    TransitionState.HasOverscrollProperties by swipeAnimation {
@@ -484,7 +515,7 @@ private class ChangeSceneSwipeTransition(
    }

    override val currentScene: SceneKey
        get() = swipeAnimation.currentContent.key
        get() = swipeAnimation.currentContent

    override val progress: Float
        get() = swipeAnimation.progress
@@ -511,17 +542,17 @@ private class ChangeSceneSwipeTransition(

private class ShowOrHideOverlaySwipeTransition(
    val layoutState: MutableSceneTransitionLayoutStateImpl,
    val swipeAnimation: SwipeAnimation<Content>,
    val _overlay: Overlay,
    val _fromOrToScene: Scene,
    val swipeAnimation: SwipeAnimation<ContentKey>,
    overlay: OverlayKey,
    fromOrToScene: SceneKey,
    override val key: TransitionKey?,
    replacedTransition: ShowOrHideOverlaySwipeTransition?,
) :
    TransitionState.Transition.ShowOrHideOverlay(
        _overlay.key,
        _fromOrToScene.key,
        swipeAnimation.fromContent.key,
        swipeAnimation.toContent.key,
        overlay,
        fromOrToScene,
        swipeAnimation.fromContent,
        swipeAnimation.toContent,
        replacedTransition,
    ),
    TransitionState.HasOverscrollProperties by swipeAnimation {
@@ -530,8 +561,8 @@ private class ShowOrHideOverlaySwipeTransition(
    ) : this(
        layoutState = other.layoutState,
        swipeAnimation = SwipeAnimation(other.swipeAnimation),
        _overlay = other._overlay,
        _fromOrToScene = other._fromOrToScene,
        overlay = other.overlay,
        fromOrToScene = other.fromOrToScene,
        key = other.key,
        replacedTransition = other,
    )
@@ -541,7 +572,7 @@ private class ShowOrHideOverlaySwipeTransition(
    }

    override val isEffectivelyShown: Boolean
        get() = swipeAnimation.currentContent == _overlay
        get() = swipeAnimation.currentContent == overlay

    override val progress: Float
        get() = swipeAnimation.progress
@@ -568,13 +599,13 @@ private class ShowOrHideOverlaySwipeTransition(

private class ReplaceOverlaySwipeTransition(
    val layoutState: MutableSceneTransitionLayoutStateImpl,
    val swipeAnimation: SwipeAnimation<Overlay>,
    val swipeAnimation: SwipeAnimation<OverlayKey>,
    override val key: TransitionKey?,
    replacedTransition: ReplaceOverlaySwipeTransition?,
) :
    TransitionState.Transition.ReplaceOverlay(
        swipeAnimation.fromContent.key,
        swipeAnimation.toContent.key,
        swipeAnimation.fromContent,
        swipeAnimation.toContent,
        replacedTransition,
    ),
    TransitionState.HasOverscrollProperties by swipeAnimation {
@@ -592,7 +623,7 @@ private class ReplaceOverlaySwipeTransition(
    }

    override val effectivelyShownOverlay: OverlayKey
        get() = swipeAnimation.currentContent.key
        get() = swipeAnimation.currentContent

    override val progress: Float
        get() = swipeAnimation.progress
+3 −4

File changed.

Preview size limit exceeded, changes collapsed.

+42 −2

File changed.

Preview size limit exceeded, changes collapsed.

Loading