Add STLState.isTransitioning(from?, to?)
This CL adds a SceneTransitionLayoutState.isTransitioning() utility to
easily check whether we are transitiong (optionally between two scenes).
We also pass the STLState in SceneScope so that scenes can easily access
it.
Bug: 291071158
Test: SceneTransitionLayoutStateTest
Flag: NA
Change-Id: Ic5def4bd28222004ad172f24648b589d1819cca4
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 1a79522..857a596 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -76,6 +76,8 @@
private val layoutImpl: SceneTransitionLayoutImpl,
private val scene: Scene,
) : SceneScope {
+ override val layoutState: SceneTransitionLayoutState = layoutImpl.state
+
override fun Modifier.element(key: ElementKey): Modifier {
return element(layoutImpl, scene, key)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 0f2c2f6..30d13df 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -95,6 +95,9 @@
@ElementDsl
interface SceneScope {
+ /** The state of the [SceneTransitionLayout] in which this scene is contained. */
+ val layoutState: SceneTransitionLayoutState
+
/**
* Tag an element identified by [key].
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index b9f83c5..64c9775 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -30,6 +30,22 @@
*/
var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene))
internal set
+
+ /**
+ * Whether we are transitioning, optionally restricting the check to the transition between
+ * [from] and [to].
+ */
+ fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+ val transition = transitionState as? TransitionState.Transition ?: return false
+
+ // TODO(b/310915136): Remove this check.
+ if (transition.fromScene == transition.toScene) {
+ return false
+ }
+
+ return (from == null || transition.fromScene == from) &&
+ (to == null || transition.toScene == to)
+ }
}
sealed interface TransitionState {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
new file mode 100644
index 0000000..94c51ca
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SceneTransitionLayoutStateTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun isTransitioningTo_idle() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+
+ assertThat(state.isTransitioning()).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
+ .isFalse()
+ }
+
+ @Test
+ fun isTransitioningTo_fromSceneEqualToToScene() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+ state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneA)
+
+ assertThat(state.isTransitioning()).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB))
+ .isFalse()
+ }
+
+ @Test
+ fun isTransitioningTo_transition() {
+ val state = SceneTransitionLayoutState(TestScenes.SceneA)
+ state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB)
+
+ assertThat(state.isTransitioning()).isTrue()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA)).isTrue()
+ assertThat(state.isTransitioning(from = TestScenes.SceneB)).isFalse()
+ assertThat(state.isTransitioning(to = TestScenes.SceneB)).isTrue()
+ assertThat(state.isTransitioning(to = TestScenes.SceneA)).isFalse()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
+ }
+
+ private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition {
+ return object : TransitionState.Transition {
+ override val currentScene: SceneKey = from
+ override val fromScene: SceneKey = from
+ override val toScene: SceneKey = to
+ override val progress: Float = 0f
+ override val isInitiatedByUserInput: Boolean = false
+ override val isUserInputOngoing: Boolean = false
+ }
+ }
+}