diff options
14 files changed, 232 insertions, 106 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 6d9497dac8ea..b9baa7930b4e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.SceneScope import com.android.systemui.R import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel @@ -81,7 +82,7 @@ constructor( .asStateFlow() @Composable - override fun Content( + override fun SceneScope.Content( modifier: Modifier, ) = BouncerScene(viewModel, dialogFactory, modifier) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index ab7bc26d59e1..ca7352ef2501 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.SceneScope import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.dagger.SysUISingleton @@ -66,7 +67,7 @@ constructor( ) @Composable - override fun Content( + override fun SceneScope.Content( modifier: Modifier, ) { LockscreenScene( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 130395a38512..29763c2e329d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel import com.android.systemui.scene.shared.model.Direction @@ -57,7 +58,7 @@ constructor( .asStateFlow() @Composable - override fun Content( + override fun SceneScope.Content( modifier: Modifier, ) { QuickSettingsScene( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt index a21366695f66..3da6a02d08d3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt @@ -18,9 +18,10 @@ package com.android.systemui.scene.ui.composable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.SceneScope import com.android.systemui.scene.shared.model.Scene /** Compose-capable extension of [Scene]. */ interface ComposableScene : Scene { - @Composable fun Content(modifier: Modifier) + @Composable fun SceneScope.Content(modifier: Modifier) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index 007055221691..774c409f34df 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -23,6 +23,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey @@ -50,7 +51,7 @@ class GoneScene @Inject constructor() : ComposableScene { .asStateFlow() @Composable - override fun Content( + override fun SceneScope.Content( modifier: Modifier, ) { /* diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 49e2bf97b3f0..3dfdbbaaee77 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -14,32 +14,31 @@ * limitations under the License. */ -@file:OptIn(ExperimentalAnimationApi::class) - package com.android.systemui.scene.ui.composable -import androidx.activity.compose.BackHandler -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button -import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.Back +import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState +import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey +import com.android.compose.animation.scene.SceneTransitionLayout +import com.android.compose.animation.scene.SceneTransitionLayoutState +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction +import com.android.compose.animation.scene.observableTransitionState +import com.android.compose.animation.scene.transitions import com.android.systemui.scene.shared.model.Direction +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.UserAction import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import java.util.Locale +import kotlinx.coroutines.flow.map /** * Renders a container of a collection of "scenes" that the user can switch between using certain @@ -64,77 +63,94 @@ fun SceneContainer( sceneByKey: Map<SceneKey, ComposableScene>, modifier: Modifier = Modifier, ) { - val currentScene: SceneModel by viewModel.currentScene.collectAsState() + val currentSceneModel: SceneModel by viewModel.currentScene.collectAsState() + val currentSceneKey = currentSceneModel.key + val currentScene = checkNotNull(sceneByKey[currentSceneKey]) + val currentDestinations: Map<UserAction, SceneModel> by + currentScene.destinationScenes().collectAsState() + val state = remember { SceneTransitionLayoutState(currentSceneKey.toTransitionSceneKey()) } + + DisposableEffect(viewModel, state) { + viewModel.setTransitionState(state.observableTransitionState().map { it.toModel() }) + onDispose { viewModel.setTransitionState(null) } + } - AnimatedContent( - targetState = currentScene.key, - label = "scene container", - modifier = modifier, - ) { currentSceneKey -> - sceneByKey.forEach { (key, composableScene) -> - if (key == currentSceneKey) { - Scene( - scene = composableScene, - onSceneChanged = viewModel::setCurrentScene, - modifier = Modifier.fillMaxSize(), - ) + SceneTransitionLayout( + currentScene = currentSceneKey.toTransitionSceneKey(), + onChangeScene = { sceneKey -> viewModel.setCurrentScene(sceneKey.toModel()) }, + transitions = transitions {}, + state = state, + modifier = modifier.fillMaxSize(), + ) { + sceneByKey.forEach { (sceneKey, composableScene) -> + scene( + key = sceneKey.toTransitionSceneKey(), + userActions = + if (sceneKey == currentSceneKey) { + currentDestinations + } else { + composableScene.destinationScenes().value + } + .map { (userAction, destinationSceneModel) -> + toTransitionModels(userAction, destinationSceneModel) + } + .toMap(), + ) { + with(composableScene) { + this@scene.Content( + modifier = Modifier.fillMaxSize(), + ) + } } } } } -/** Renders the given [ComposableScene]. */ -@Composable -private fun Scene( - scene: ComposableScene, - onSceneChanged: (SceneModel) -> Unit, - modifier: Modifier = Modifier, -) { - val destinationScenes: Map<UserAction, SceneModel> by scene.destinationScenes().collectAsState() - val swipeLeftDestinationScene = destinationScenes[UserAction.Swipe(Direction.LEFT)] - val swipeUpDestinationScene = destinationScenes[UserAction.Swipe(Direction.UP)] - val swipeRightDestinationScene = destinationScenes[UserAction.Swipe(Direction.RIGHT)] - val swipeDownDestinationScene = destinationScenes[UserAction.Swipe(Direction.DOWN)] - val backDestinationScene = destinationScenes[UserAction.Back] - - // TODO(b/280880714): replace with the real UI and make sure to call onTransitionProgress. - Box(modifier) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.align(Alignment.Center), - ) { - scene.Content( - modifier = Modifier, +// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout. +private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransitionState { + return when (this) { + is SceneTransitionObservableTransitionState.Idle -> + ObservableTransitionState.Idle(scene.toModel().key) + is SceneTransitionObservableTransitionState.Transition -> + ObservableTransitionState.Transition( + fromScene = fromScene.toModel().key, + toScene = toScene.toModel().key, + progress = progress, ) + } +} - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - DirectionalButton(Direction.LEFT, swipeLeftDestinationScene, onSceneChanged) - DirectionalButton(Direction.UP, swipeUpDestinationScene, onSceneChanged) - DirectionalButton(Direction.RIGHT, swipeRightDestinationScene, onSceneChanged) - DirectionalButton(Direction.DOWN, swipeDownDestinationScene, onSceneChanged) - } +// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout. +private fun toTransitionModels( + userAction: UserAction, + sceneModel: SceneModel, +): Pair<SceneTransitionUserAction, SceneTransitionSceneKey> { + return userAction.toTransitionUserAction() to sceneModel.key.toTransitionSceneKey() +} - if (backDestinationScene != null) { - BackHandler { onSceneChanged.invoke(backDestinationScene) } - } - } - } +// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout. +private fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey { + return SceneTransitionSceneKey( + name = toString(), + identity = this, + ) } -@Composable -private fun DirectionalButton( - direction: Direction, - destinationScene: SceneModel?, - onSceneChanged: (SceneModel) -> Unit, - modifier: Modifier = Modifier, -) { - Button( - onClick = { destinationScene?.let { onSceneChanged.invoke(it) } }, - enabled = destinationScene != null, - modifier = modifier, - ) { - Text(direction.name.lowercase(Locale.getDefault())) +// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout. +private fun SceneTransitionSceneKey.toModel(): SceneModel { + return SceneModel(key = identity as SceneKey) +} + +// TODO(b/293899074): remove this once we can use the one from SceneTransitionLayout. +private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction { + return when (this) { + is UserAction.Swipe -> + when (this.direction) { + Direction.LEFT -> Swipe.Left + Direction.UP -> Swipe.Up + Direction.RIGHT -> Swipe.Right + Direction.DOWN -> Swipe.Down + } + is UserAction.Back -> Back } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index b73e0b26f208..ff1cb5f1afa3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.notifications.ui.composable.Notifications @@ -63,7 +64,7 @@ constructor( ) @Composable - override fun Content( + override fun SceneScope.Content( modifier: Modifier, ) = ShadeScene(viewModel, modifier) @@ -86,11 +87,12 @@ private fun ShadeScene( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), modifier = - Modifier.fillMaxSize() + modifier + .fillMaxSize() .clickable(onClick = { viewModel.onContentClicked() }) .padding(horizontal = 16.dp, vertical = 48.dp) ) { - QuickSettings(modifier = modifier.height(160.dp)) - Notifications(modifier = modifier.weight(1f)) + QuickSettings(modifier = Modifier.height(160.dp)) + Notifications(modifier = Modifier.weight(1f)) } } 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 1fca4886a31f..fee3960ff0e1 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 @@ -14,16 +14,23 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.scene.data.repository +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneTransitionModel import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf /** Source of truth for scene framework application state. */ class SceneContainerRepository @@ -38,8 +45,17 @@ constructor( private val _currentScene = MutableStateFlow(SceneModel(config.initialSceneKey)) val currentScene: StateFlow<SceneModel> = _currentScene.asStateFlow() - private val _transitionProgress = MutableStateFlow(1f) - val transitionProgress: StateFlow<Float> = _transitionProgress.asStateFlow() + private val transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null) + val transitionProgress: Flow<Float> = + transitionState.flatMapLatest { observableTransitionStateFlow -> + observableTransitionStateFlow?.flatMapLatest { observableTransitionState -> + when (observableTransitionState) { + is ObservableTransitionState.Idle -> flowOf(1f) + is ObservableTransitionState.Transition -> observableTransitionState.progress + } + } + ?: flowOf(1f) + } private val _transitions = MutableStateFlow<SceneTransitionModel?>(null) val transitions: StateFlow<SceneTransitionModel?> = _transitions.asStateFlow() @@ -92,8 +108,12 @@ constructor( _isVisible.value = isVisible } - /** Sets scene transition progress to the current scene in the container with the given name. */ - fun setSceneTransitionProgress(progress: Float) { - _transitionProgress.value = progress + /** + * Binds the given flow so the system remembers it. + * + * Note that you must call is with `null` when the UI is done or risk a memory leak. + */ + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { + this.transitionState.value = transitionState } } 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 39daad33f75e..b09a5cf087cb 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 @@ -18,11 +18,13 @@ package com.android.systemui.scene.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.data.repository.SceneContainerRepository +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneTransitionModel import javax.inject.Inject +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -69,13 +71,17 @@ constructor( /** Whether the container with the given name is visible. */ val isVisible: StateFlow<Boolean> = repository.isVisible - /** Sets scene transition progress to the current scene in the container with the given name. */ - fun setSceneTransitionProgress(progress: Float) { - repository.setSceneTransitionProgress(progress) + /** + * Binds the given flow so the system remembers it. + * + * Note that you must call is with `null` when the UI is done or risk a memory leak. + */ + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { + repository.setTransitionState(transitionState) } /** Progress of the transition into the current scene in the container with the given name. */ - val transitionProgress: StateFlow<Float> = repository.transitionProgress + val transitionProgress: Flow<Float> = repository.transitionProgress /** * Scene transitions as pairs of keys. A new value is emitted exactly once, each time a scene diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt new file mode 100644 index 000000000000..9a30aa65068c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt @@ -0,0 +1,36 @@ +/* + * Copyright 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.systemui.scene.shared.model + +import kotlinx.coroutines.flow.Flow + +/** + * This is a fork of a class by the same name in the `com.android.compose.animation.scene` package. + * + * TODO(b/293899074): remove this fork, once we can compile Compose into System UI. + */ +sealed class ObservableTransitionState { + /** No transition/animation is currently running. */ + data class Idle(val scene: SceneKey) : ObservableTransitionState() + + /** There is a transition animating between two scenes. */ + data class Transition( + val fromScene: SceneKey, + val toScene: SceneKey, + val progress: Flow<Float>, + ) : ObservableTransitionState() +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index f44748a99080..bd73e36262b8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -19,10 +19,12 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow /** Models UI state for the scene container. */ @@ -54,9 +56,13 @@ constructor( interactor.setCurrentScene(scene) } - /** Notifies of the progress of a scene transition. */ - fun setSceneTransitionProgress(progress: Float) { - interactor.setSceneTransitionProgress(progress) + /** + * Binds the given flow so the system remembers it. + * + * Note that you must call is with `null` when the UI is done or risk a memory leak. + */ + fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) { + interactor.setTransitionState(transitionState) } /** Handles a [MotionEvent] representing remote user input. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index 826a6ccfbaec..56e3e9649fe7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -22,11 +22,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneTransitionModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -91,11 +93,30 @@ class SceneContainerRepositoryTest : SysuiTestCase() { val sceneTransitionProgress by collectLastValue(underTest.transitionProgress) assertThat(sceneTransitionProgress).isEqualTo(1f) - underTest.setSceneTransitionProgress(0.1f) + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(SceneKey.Lockscreen) + ) + underTest.setTransitionState(transitionState) + assertThat(sceneTransitionProgress).isEqualTo(1f) + + val progress = MutableStateFlow(1f) + transitionState.value = + ObservableTransitionState.Transition( + fromScene = SceneKey.Lockscreen, + toScene = SceneKey.Shade, + progress = progress, + ) + assertThat(sceneTransitionProgress).isEqualTo(1f) + + progress.value = 0.1f assertThat(sceneTransitionProgress).isEqualTo(0.1f) - underTest.setSceneTransitionProgress(0.9f) + progress.value = 0.9f assertThat(sceneTransitionProgress).isEqualTo(0.9f) + + underTest.setTransitionState(null) + assertThat(sceneTransitionProgress).isEqualTo(1f) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 13a602dcddb0..c193d830bb20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -22,11 +22,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneTransitionModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -37,7 +39,8 @@ import org.junit.runners.JUnit4 class SceneInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) - private val underTest = utils.sceneInteractor() + private val repository = utils.fakeSceneContainerRepository() + private val underTest = utils.sceneInteractor(repository = repository) @Test fun allSceneKeys() { @@ -55,11 +58,20 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun sceneTransitionProgress() = runTest { - val progress by collectLastValue(underTest.transitionProgress) - assertThat(progress).isEqualTo(1f) - - underTest.setSceneTransitionProgress(0.55f) - assertThat(progress).isEqualTo(0.55f) + val transitionProgress by collectLastValue(underTest.transitionProgress) + assertThat(transitionProgress).isEqualTo(1f) + + val progress = MutableStateFlow(0.55f) + repository.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = SceneKey.Lockscreen, + toScene = SceneKey.Shade, + progress = progress, + ), + ) + ) + assertThat(transitionProgress).isEqualTo(0.55f) } @Test diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 26a75d0cc70a..70d15a068356 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -119,9 +119,11 @@ class SceneTestUtils( ) } - fun sceneInteractor(): SceneInteractor { + fun sceneInteractor( + repository: SceneContainerRepository = fakeSceneContainerRepository() + ): SceneInteractor { return SceneInteractor( - repository = fakeSceneContainerRepository(), + repository = repository, ) } |