[flexiglass] Introduces a previousScene flow.
This is needed for bugs like the attached one and potentially other bugs
where logic needs to know not only the current scene but also the
previous scene but don't need to monitor every scene-to-scene transition
state.
Bug: 330695017
Test: added unit tests at the data and domain layers
Test: manually verified the fix for the attached bug with the next CL in
the chain
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ifb07f26f47a210d46b17fdbcc665dbc6f8c272b5
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 3d66192..ae31058 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -140,4 +140,23 @@
ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
)
}
+
+ @Test
+ fun previousScene() =
+ testScope.runTest {
+ val underTest = kosmos.sceneContainerRepository
+ val currentScene by collectLastValue(underTest.currentScene)
+ val previousScene by collectLastValue(underTest.previousScene)
+
+ assertThat(previousScene).isNull()
+
+ val firstScene = currentScene
+ underTest.changeScene(Scenes.Shade)
+ assertThat(previousScene).isEqualTo(firstScene)
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+
+ underTest.changeScene(Scenes.QuickSettings)
+ assertThat(previousScene).isEqualTo(Scenes.Shade)
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 143c0f2..b179c30 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -291,4 +291,19 @@
assertThat(isVisible).isFalse()
}
+
+ @Test
+ fun previousScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ val previousScene by collectLastValue(underTest.previousScene)
+ assertThat(previousScene).isNull()
+
+ val firstScene = currentScene
+ underTest.changeScene(toScene = Scenes.Shade, "reason")
+ assertThat(previousScene).isEqualTo(firstScene)
+
+ underTest.changeScene(toScene = Scenes.QuickSettings, "reason")
+ assertThat(previousScene).isEqualTo(Scenes.Shade)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 994b012..3082eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -24,6 +24,8 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSource
+import com.android.systemui.util.kotlin.WithPrev
+import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,6 +36,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Source of truth for scene framework application state. */
@@ -44,7 +47,32 @@
private val config: SceneContainerConfig,
private val dataSource: SceneDataSource,
) {
- val currentScene: StateFlow<SceneKey> = dataSource.currentScene
+ private val previousAndCurrentScene: StateFlow<WithPrev<SceneKey?, SceneKey>> =
+ dataSource.currentScene
+ .pairwise()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = WithPrev(null, dataSource.currentScene.value),
+ )
+
+ val currentScene: StateFlow<SceneKey> =
+ previousAndCurrentScene
+ .map { it.newValue }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = previousAndCurrentScene.value.newValue,
+ )
+
+ val previousScene: StateFlow<SceneKey?> =
+ previousAndCurrentScene
+ .map { it.previousValue }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = previousAndCurrentScene.value.previousValue,
+ )
private val _isVisible = MutableStateFlow(true)
val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 2ccd3b9..0239455 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -140,6 +140,14 @@
)
/**
+ * The previous scene.
+ *
+ * This is effectively the previous value of [currentScene] which means that all caveats, for
+ * example regarding when in a transition the current scene changes, apply.
+ */
+ val previousScene: StateFlow<SceneKey?> = repository.previousScene
+
+ /**
* Returns the keys of all scenes in the container.
*
* The scenes will be sorted in z-order such that the last one is the one that should be