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

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

Replace transitions to avoid unnecessary interruption handling

This CL adds a Transition.replacedTransition so that a transition can be
replaced without triggering the interruption support. This is for
instance used when intercepting a swipe transition.

Bug: 290930950
Test: atest ElementTest
Test: atest DraggableHandlerTest
Flag: com.android.systemui.scene_container
Change-Id: I864f217b1eb62abc970323daf076f1c0af71c764
parent 042ca77a
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -48,8 +48,15 @@ internal fun CoroutineScope.animateToScene(
    }

    return when (transitionState) {
        is TransitionState.Idle ->
            animate(layoutState, target, transitionKey, isInitiatedByUserInput = false)
        is TransitionState.Idle -> {
            animate(
                layoutState,
                target,
                transitionKey,
                isInitiatedByUserInput = false,
                replacedTransition = null,
            )
        }
        is TransitionState.Transition -> {
            val isInitiatedByUserInput = transitionState.isInitiatedByUserInput

@@ -79,6 +86,7 @@ internal fun CoroutineScope.animateToScene(
                        isInitiatedByUserInput,
                        initialProgress = progress,
                        initialVelocity = transitionState.progressVelocity,
                        replacedTransition = transitionState,
                    )
                }
            } else if (transitionState.fromScene == target) {
@@ -101,6 +109,7 @@ internal fun CoroutineScope.animateToScene(
                        initialProgress = progress,
                        initialVelocity = transitionState.progressVelocity,
                        reversed = true,
                        replacedTransition = transitionState,
                    )
                }
            } else {
@@ -137,6 +146,7 @@ internal fun CoroutineScope.animateToScene(
                    isInitiatedByUserInput,
                    fromScene = animateFrom,
                    chain = chain,
                    replacedTransition = null,
                )
            }
        }
@@ -148,6 +158,7 @@ private fun CoroutineScope.animate(
    targetScene: SceneKey,
    transitionKey: TransitionKey?,
    isInitiatedByUserInput: Boolean,
    replacedTransition: TransitionState.Transition?,
    initialProgress: Float = 0f,
    initialVelocity: Float = 0f,
    reversed: Boolean = false,
@@ -164,6 +175,7 @@ private fun CoroutineScope.animate(
                currentScene = targetScene,
                isInitiatedByUserInput = isInitiatedByUserInput,
                isUserInputOngoing = false,
                replacedTransition = replacedTransition,
            )
        } else {
            OneOffTransition(
@@ -173,6 +185,7 @@ private fun CoroutineScope.animate(
                currentScene = targetScene,
                isInitiatedByUserInput = isInitiatedByUserInput,
                isUserInputOngoing = false,
                replacedTransition = replacedTransition,
            )
        }

@@ -214,7 +227,8 @@ private class OneOffTransition(
    override val currentScene: SceneKey,
    override val isInitiatedByUserInput: Boolean,
    override val isUserInputOngoing: Boolean,
) : TransitionState.Transition(fromScene, toScene) {
    replacedTransition: TransitionState.Transition?,
) : TransitionState.Transition(fromScene, toScene, replacedTransition) {
    /**
     * The animatable used to animate this transition.
     *
+6 −3
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

interface DraggableHandler {
internal interface DraggableHandler {
    /**
     * Start a drag in the given [startedPosition], with the given [overSlop] and number of
     * [pointersDown].
@@ -51,7 +51,7 @@ interface DraggableHandler {
 * The [DragController] provides control over the transition between two scenes through the [onDrag]
 * and [onStop] methods.
 */
interface DragController {
internal interface DragController {
    /** Drag the current scene by [delta] pixels. */
    fun onDrag(delta: Float)

@@ -537,6 +537,7 @@ private fun SwipeTransition(
        orientation = orientation,
        isUpOrLeft = isUpOrLeft,
        requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
        replacedTransition = null,
    )
}

@@ -553,6 +554,7 @@ private fun SwipeTransition(old: SwipeTransition): SwipeTransition {
            isUpOrLeft = old.isUpOrLeft,
            lastDistance = old.lastDistance,
            requiresFullDistanceSwipe = old.requiresFullDistanceSwipe,
            replacedTransition = old,
        )
        .apply {
            _currentScene = old._currentScene
@@ -571,9 +573,10 @@ private class SwipeTransition(
    override val orientation: Orientation,
    override val isUpOrLeft: Boolean,
    val requiresFullDistanceSwipe: Boolean,
    replacedTransition: SwipeTransition?,
    var lastDistance: Float = DistanceUnspecified,
) :
    TransitionState.Transition(_fromScene.key, _toScene.key),
    TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition),
    TransitionState.HasOverscrollProperties {
    var _currentScene by mutableStateOf(_fromScene)
    override val currentScene: SceneKey
+4 −0
Original line number Diff line number Diff line
@@ -495,6 +495,10 @@ private fun prepareInterruption(
    transition: TransitionState.Transition,
    previousTransition: TransitionState.Transition,
) {
    if (transition.replacedTransition == previousTransition) {
        return
    }

    val sceneStates = element.sceneStates
    fun updatedSceneState(key: SceneKey): Element.SceneState? {
        return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() }
+16 −0
Original line number Diff line number Diff line
@@ -224,6 +224,9 @@ sealed interface TransitionState {

        /** The scene this transition is going to. Can't be the same as fromScene */
        val toScene: SceneKey,

        /** The transition that `this` transition is replacing, if any. */
        internal val replacedTransition: Transition? = null,
    ) : TransitionState {
        /**
         * The key of this transition. This should usually be null, but it can be specified to use a
@@ -279,6 +282,11 @@ sealed interface TransitionState {

        init {
            check(fromScene != toScene)
            check(
                replacedTransition == null ||
                    (replacedTransition.fromScene == fromScene &&
                        replacedTransition.toScene == toScene)
            )
        }

        /**
@@ -321,6 +329,10 @@ sealed interface TransitionState {
                return 0f
            }

            if (replacedTransition != null) {
                return replacedTransition.interruptionProgress(layoutImpl)
            }

            fun create(): Animatable<Float, AnimationVector1D> {
                val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
                layoutImpl.coroutineScope.launch {
@@ -521,6 +533,10 @@ internal abstract class BaseSceneTransitionLayoutState(
                    check(transitionStates.size == 1)
                    check(transitionStates[0] is TransitionState.Idle)
                    transitionStates = listOf(transition)
                } else if (currentState == transition.replacedTransition) {
                    // Replace the transition.
                    transitionStates =
                        transitionStates.subList(0, transitionStates.lastIndex) + transition
                } else {
                    // Append the new transition.
                    transitionStates = transitionStates + transition
+13 −0
Original line number Diff line number Diff line
@@ -1233,4 +1233,17 @@ class DraggableHandlerTest {
        advanceUntilIdle()
        assertIdle(SceneB)
    }

    @Test
    fun interceptingTransitionReplacesCurrentTransition() = runGestureTest {
        val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f))
        val transition = assertThat(layoutState.transitionState).isTransition()
        controller.onDragStopped(velocity = 0f)

        // Intercept the transition.
        onDragStartedImmediately()
        val newTransition = assertThat(layoutState.transitionState).isTransition()
        assertThat(newTransition).isNotSameInstanceAs(transition)
        assertThat(newTransition.replacedTransition).isSameInstanceAs(transition)
    }
}
Loading