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

Commit eed749f2 authored by omarmt's avatar omarmt
Browse files

STL add support for overlays in snapToIdleIfClose()

If the transition is close to the current Content, we can immediately
switch to the Idle state.

Test: atest SceneTransitionLayoutStateTest
Bug: 377503459
Flag: com.android.systemui.scene_container
Change-Id: I0b0df74516d550a0618ef3118c5ac74d5fb3a96a
parent 0f6fd445
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -614,8 +614,8 @@ internal class MutableSceneTransitionLayoutStateImpl(
        }

        val shouldSnap =
            (isProgressCloseTo(0f) && transition.currentScene == transition.fromContent) ||
                (isProgressCloseTo(1f) && transition.currentScene == transition.toContent)
            (isProgressCloseTo(0f) && transition.isFromCurrentContent()) ||
                (isProgressCloseTo(1f) && transition.isToCurrentContent())
        return if (shouldSnap) {
            finishAllTransitions()
            true
+17 −2
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ sealed interface TransitionState {
             * starting a swipe transition to show [overlay] and will be `true` only once the swipe
             * transition is committed.
             */
            protected abstract val isEffectivelyShown: Boolean
            abstract val isEffectivelyShown: Boolean

            init {
                check(
@@ -164,7 +164,7 @@ sealed interface TransitionState {
             * [fromOverlay] by [toOverlay] and will [toOverlay] once the swipe transition is
             * committed.
             */
            protected abstract val effectivelyShownOverlay: OverlayKey
            abstract val effectivelyShownOverlay: OverlayKey

            init {
                check(fromOverlay != toOverlay)
@@ -327,6 +327,21 @@ sealed interface TransitionState {
            }
        }

        /** Whether [fromContent] is effectively the current content of the transition. */
        internal fun isFromCurrentContent() = isCurrentContent(expectedFrom = true)

        /** Whether [toContent] is effectively the current content of the transition. */
        internal fun isToCurrentContent() = isCurrentContent(expectedFrom = false)

        private fun isCurrentContent(expectedFrom: Boolean): Boolean {
            val expectedContent = if (expectedFrom) fromContent else toContent
            return when (this) {
                is ChangeScene -> currentScene == expectedContent
                is ReplaceOverlay -> effectivelyShownOverlay == expectedContent
                is ShowOrHideOverlay -> isEffectivelyShown == (expectedContent == overlay)
            }
        }

        /** Run this transition and return once it is finished. */
        abstract suspend fun run()

+39 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestOverlays.OverlayA
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
@@ -326,6 +327,25 @@ class SceneTransitionLayoutStateTest {
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
    }

    @Test
    fun snapToIdleIfClose_snapToStart_overlays() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
        state.startTransitionImmediately(
            animationScope = backgroundScope,
            transition(SceneA, OverlayA, isEffectivelyShown = { false }, progress = { 0.2f }),
        )
        assertThat(state.isTransitioning()).isTrue()

        // Ignore the request if the progress is not close to 0 or 1, using the threshold.
        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
        assertThat(state.isTransitioning()).isTrue()

        // Go to the initial scene if it is close to 0.
        assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
        assertThat(state.isTransitioning()).isFalse()
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
    }

    @Test
    fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
@@ -345,6 +365,25 @@ class SceneTransitionLayoutStateTest {
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
    }

    @Test
    fun snapToIdleIfClose_snapToEnd_overlays() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
        state.startTransitionImmediately(
            animationScope = backgroundScope,
            transition(SceneA, OverlayA, isEffectivelyShown = { true }, progress = { 0.8f }),
        )
        assertThat(state.isTransitioning()).isTrue()

        // Ignore the request if the progress is not close to 0 or 1, using the threshold.
        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
        assertThat(state.isTransitioning()).isTrue()

        // Go to the final scene if it is close to 1.
        assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
        assertThat(state.isTransitioning()).isFalse()
        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA, setOf(OverlayA)))
    }

    @Test
    fun snapToIdleIfClose_multipleTransitions() = runMonotonicClockTest {
        val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)