Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +0 −34 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastForEach import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.SharedElementTransformation import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job Loading Loading @@ -528,39 +527,6 @@ internal class MutableSceneTransitionLayoutStateImpl( transitionStates = listOf(TransitionState.Idle(scene, currentOverlays)) } /** * Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap * to the closest scene. * * Important: Snapping to the closest scene will instantly finish *all* ongoing transitions, * only the progress of the last transition will be checked. * * @return true if snapped to the closest scene. */ internal fun snapToIdleIfClose(threshold: Float): Boolean { val transition = currentTransition ?: return false val progress = transition.progress fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold fun finishAllTransitions() { // Force finish all transitions. while (currentTransitions.isNotEmpty()) { finishTransition(transitionStates[0] as TransitionState.Transition) } } val shouldSnap = (isProgressCloseTo(0f) && transition.isFromCurrentContent()) || (isProgressCloseTo(1f) && transition.isToCurrentContent()) return if (shouldSnap) { finishAllTransitions() true } else { false } } override fun showOverlay( overlay: OverlayKey, animationScope: CoroutineScope, Loading packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +0 −127 Original line number Diff line number Diff line Loading @@ -18,12 +18,9 @@ package com.android.compose.animation.scene import android.util.Log import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.getValue 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 Loading Loading @@ -169,130 +166,6 @@ class SceneTransitionLayoutStateTest { assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2) } @Test fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) state.startTransitionImmediately( animationScope = backgroundScope, transition(from = SceneA, to = SceneB, current = { SceneA }, 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_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) state.startTransitionImmediately( animationScope = backgroundScope, transition(from = SceneA, to = SceneB, 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(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) val aToB = transition(from = SceneA, to = SceneB, progress = { 0.5f }) state.startTransitionImmediately(animationScope = backgroundScope, aToB) assertThat(state.currentTransitions).containsExactly(aToB).inOrder() val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f }) state.startTransitionImmediately(animationScope = backgroundScope, bToC) assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder() // 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.currentTransitions).containsExactly(aToB, bToC).inOrder() // Go to the final scene if it is close to 1. assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue() assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC)) assertThat(state.currentTransitions).isEmpty() } @Test fun snapToIdleIfClose_closeButNotCurrentScene() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) var progress by mutableStateOf(0f) var currentScene by mutableStateOf(SceneB) state.startTransitionImmediately( animationScope = backgroundScope, transition( from = SceneA, to = SceneB, current = { currentScene }, progress = { progress }, ), ) assertThat(state.isTransitioning()).isTrue() // Ignore the request if we are close to a scene that is not the current scene assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse() assertThat(state.isTransitioning()).isTrue() progress = 1f currentScene = SceneA assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse() assertThat(state.isTransitioning()).isTrue() } private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB( progress: () -> Float, sceneTransitions: SceneTransitions, Loading Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +0 −34 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastForEach import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.SharedElementTransformation import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job Loading Loading @@ -528,39 +527,6 @@ internal class MutableSceneTransitionLayoutStateImpl( transitionStates = listOf(TransitionState.Idle(scene, currentOverlays)) } /** * Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap * to the closest scene. * * Important: Snapping to the closest scene will instantly finish *all* ongoing transitions, * only the progress of the last transition will be checked. * * @return true if snapped to the closest scene. */ internal fun snapToIdleIfClose(threshold: Float): Boolean { val transition = currentTransition ?: return false val progress = transition.progress fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold fun finishAllTransitions() { // Force finish all transitions. while (currentTransitions.isNotEmpty()) { finishTransition(transitionStates[0] as TransitionState.Transition) } } val shouldSnap = (isProgressCloseTo(0f) && transition.isFromCurrentContent()) || (isProgressCloseTo(1f) && transition.isToCurrentContent()) return if (shouldSnap) { finishAllTransitions() true } else { false } } override fun showOverlay( overlay: OverlayKey, animationScope: CoroutineScope, Loading
packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +0 −127 Original line number Diff line number Diff line Loading @@ -18,12 +18,9 @@ package com.android.compose.animation.scene import android.util.Log import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.getValue 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 Loading Loading @@ -169,130 +166,6 @@ class SceneTransitionLayoutStateTest { assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2) } @Test fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) state.startTransitionImmediately( animationScope = backgroundScope, transition(from = SceneA, to = SceneB, current = { SceneA }, 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_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) state.startTransitionImmediately( animationScope = backgroundScope, transition(from = SceneA, to = SceneB, 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(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) val aToB = transition(from = SceneA, to = SceneB, progress = { 0.5f }) state.startTransitionImmediately(animationScope = backgroundScope, aToB) assertThat(state.currentTransitions).containsExactly(aToB).inOrder() val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f }) state.startTransitionImmediately(animationScope = backgroundScope, bToC) assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder() // 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.currentTransitions).containsExactly(aToB, bToC).inOrder() // Go to the final scene if it is close to 1. assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue() assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC)) assertThat(state.currentTransitions).isEmpty() } @Test fun snapToIdleIfClose_closeButNotCurrentScene() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) var progress by mutableStateOf(0f) var currentScene by mutableStateOf(SceneB) state.startTransitionImmediately( animationScope = backgroundScope, transition( from = SceneA, to = SceneB, current = { currentScene }, progress = { progress }, ), ) assertThat(state.isTransitioning()).isTrue() // Ignore the request if we are close to a scene that is not the current scene assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse() assertThat(state.isTransitioning()).isTrue() progress = 1f currentScene = SceneA assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse() assertThat(state.isTransitioning()).isTrue() } private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB( progress: () -> Float, sceneTransitions: SceneTransitions, Loading