diff options
19 files changed, 167 insertions, 90 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index a1d8c29c2a39..bdd888f45182 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -8,12 +8,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.FixedSizeEdgeDetector import com.android.compose.animation.scene.LowestZIndexScenePicker +import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout @@ -21,12 +24,13 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions -import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.SceneDataSourceDelegator +import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource import com.android.systemui.statusbar.phone.SystemUIDialogFactory object Communal { @@ -63,27 +67,35 @@ val sceneTransitions = transitions { fun CommunalContainer( modifier: Modifier = Modifier, viewModel: CommunalViewModel, + dataSourceDelegator: SceneDataSourceDelegator, dialogFactory: SystemUIDialogFactory, ) { - val currentScene: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank) - val sceneTransitionLayoutState = - updateSceneTransitionLayoutState( - currentScene, - onChangeScene = { viewModel.onSceneChanged(it) }, + val coroutineScope = rememberCoroutineScope() + val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank) + val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false) + val state: MutableSceneTransitionLayoutState = remember { + MutableSceneTransitionLayoutState( + initialScene = currentSceneKey, transitions = sceneTransitions, enableInterruptions = false, ) - val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false) + } + + DisposableEffect(state) { + val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope) + dataSourceDelegator.setDelegate(dataSource) + onDispose { dataSourceDelegator.setDelegate(null) } + } // This effect exposes the SceneTransitionLayout's observable transition state to the rest of // the system, and unsets it when the view is disposed to avoid a memory leak. - DisposableEffect(viewModel, sceneTransitionLayoutState) { - viewModel.setTransitionState(sceneTransitionLayoutState.observableTransitionState()) + DisposableEffect(viewModel, state) { + viewModel.setTransitionState(state.observableTransitionState()) onDispose { viewModel.setTransitionState(null) } } SceneTransitionLayout( - state = sceneTransitionLayoutState, + state = state, modifier = modifier.fillMaxSize(), swipeSourceDetector = FixedSizeEdgeDetector( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index 43266bfcbc55..a944afb70f38 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -88,7 +88,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -158,7 +158,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -179,7 +179,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { val scene by collectLastValue(communalInteractor.desiredScene) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) assertThat(scene).isEqualTo(CommunalScenes.Communal) fakeKeyguardTransitionRepository.sendTransitionSteps( @@ -207,7 +207,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fun dockingOnLockscreen_forcesCommunal() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -229,7 +229,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() = with(kosmos) { testScope.runTest { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) val scene by collectLastValue(communalInteractor.desiredScene) // device is docked while on the lockscreen @@ -261,7 +261,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -278,7 +278,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is not dreaming and on communal. fakeKeyguardRepository.setDreaming(false) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) // Scene stays as Communal advanceTimeBy(SCREEN_TIMEOUT.milliseconds) @@ -293,7 +293,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -316,7 +316,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is on communal, but not dreaming. fakeKeyguardRepository.setDreaming(false) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -338,7 +338,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { testScope.runTest { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) @@ -367,7 +367,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { // Device is dreaming and on communal. fakeKeyguardRepository.setDreaming(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) assertThat(scene).isEqualTo(CommunalScenes.Communal) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt index 43acf3197fb1..2d78a9b9d808 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt @@ -22,36 +22,26 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.data.repository.sceneContainerRepository -import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags +import com.android.systemui.scene.shared.model.sceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class CommunalRepositoryImplTest : SysuiTestCase() { - private lateinit var underTest: CommunalRepositoryImpl private val kosmos = testKosmos() private val testScope = kosmos.testScope - private val sceneContainerRepository = kosmos.sceneContainerRepository - - @Before - fun setUp() { - underTest = createRepositoryImpl(false) - } - - private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl { - return CommunalRepositoryImpl( - testScope.backgroundScope, - kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled }, - sceneContainerRepository, + private val underTest by lazy { + CommunalRepositoryImpl( + kosmos.applicationCoroutineScope, + kosmos.sceneDataSource, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 8e9d7690f215..e7ccde26e161 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -482,7 +482,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank) val targetScene = CommunalScenes.Communal - communalRepository.setDesiredScene(targetScene) + communalRepository.changeScene(targetScene) desiredScene = collectLastValue(underTest.desiredScene) runCurrent() assertThat(desiredScene()).isEqualTo(targetScene) @@ -493,9 +493,9 @@ class CommunalInteractorTest : SysuiTestCase() { testScope.runTest { val targetScene = CommunalScenes.Communal - underTest.onSceneChanged(targetScene) + underTest.changeScene(targetScene) - val desiredScene = collectLastValue(communalRepository.desiredScene) + val desiredScene = collectLastValue(communalRepository.currentScene) runCurrent() assertThat(desiredScene()).isEqualTo(targetScene) } @@ -508,7 +508,7 @@ class CommunalInteractorTest : SysuiTestCase() { val desiredScene by collectLastValue(underTest.desiredScene) - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) assertThat(desiredScene).isEqualTo(CommunalScenes.Communal) kosmos.setCommunalAvailable(false) @@ -659,7 +659,7 @@ class CommunalInteractorTest : SysuiTestCase() { runCurrent() assertThat(isCommunalShowing()).isEqualTo(false) - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) isCommunalShowing = collectLastValue(underTest.isCommunalShowing) runCurrent() @@ -683,12 +683,12 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes (without the flag) to communal sets the value to true - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isTrue() // Verify scene changes (without the flag) to blank sets the value back to false - underTest.onSceneChanged(CommunalScenes.Blank) + underTest.changeScene(CommunalScenes.Blank) runCurrent() assertThat(isCommunalShowing).isFalse() } @@ -704,7 +704,7 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(isCommunalShowing).isFalse() // Verify scene changes without the flag doesn't have any impact - underTest.onSceneChanged(CommunalScenes.Communal) + underTest.changeScene(CommunalScenes.Communal) runCurrent() assertThat(isCommunalShowing).isFalse() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt index 50b8da62b3f0..3a23e14e2777 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -158,7 +158,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { kosmos.setCommunalAvailable(true) communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED) } @@ -171,7 +171,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } @@ -184,13 +184,13 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { goToCommunal() communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED) } private suspend fun goToCommunal() { kosmos.setCommunalAvailable(true) - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index ef686f91b36a..4d328d6cb13f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -72,7 +72,7 @@ constructor( .filterNotNull() // TODO(b/322787129): Also set a custom transition animation here to avoid the regular // slide-in animation when setting the scene programmatically - .onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) } + .onEach { nextScene -> communalInteractor.changeScene(nextScene) } .launchIn(applicationScope) // TODO(b/322787129): re-enable once custom animations are in place @@ -129,7 +129,7 @@ constructor( .sample(keyguardInteractor.isDreaming, ::Pair) .collect { (shouldTimeout, isDreaming) -> if (isDreaming && shouldTimeout) { - communalInteractor.onSceneChanged(CommunalScenes.Blank) + communalInteractor.changeScene(CommunalScenes.Blank) } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt new file mode 100644 index 000000000000..5e41a1b8a9b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/Communal.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 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.communal.dagger + +import javax.inject.Qualifier + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Communal diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt index 82d943796e2a..72dcb26b089a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -23,11 +23,19 @@ import com.android.systemui.communal.data.repository.CommunalRepositoryModule import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.widgets.CommunalWidgetModule import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl +import com.android.systemui.dagger.SysUISingleton +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.scene.shared.model.SceneDataSourceDelegator import dagger.Binds import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineScope @Module( includes = @@ -47,4 +55,24 @@ interface CommunalModule { fun bindEditWidgetsActivityStarter( starter: EditWidgetsActivityStarterImpl ): EditWidgetsActivityStarter + + @Binds + @Communal + fun bindCommunalSceneDataSource(@Communal delegator: SceneDataSourceDelegator): SceneDataSource + + companion object { + @Provides + @Communal + @SysUISingleton + fun providesCommunalSceneDataSourceDelegator( + @Application applicationScope: CoroutineScope + ): SceneDataSourceDelegator { + val config = + SceneContainerConfig( + sceneKeys = listOf(CommunalScenes.Blank, CommunalScenes.Communal), + initialSceneKey = CommunalScenes.Blank + ) + return SceneDataSourceDelegator(applicationScope, config) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt index 201ce832cc41..8bfd8d91dfca 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt @@ -18,11 +18,12 @@ package com.android.systemui.communal.data.repository import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey +import com.android.systemui.communal.dagger.Communal import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.scene.data.repository.SceneContainerRepository -import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.scene.shared.model.SceneDataSource import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -30,7 +31,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn @@ -38,16 +38,15 @@ import kotlinx.coroutines.flow.stateIn /** Encapsulates the state of communal mode. */ interface CommunalRepository { /** - * Target scene as requested by the underlying [SceneTransitionLayout] or through - * [setDesiredScene]. + * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene]. */ - val desiredScene: StateFlow<SceneKey> + val currentScene: StateFlow<SceneKey> /** Exposes the transition state of the communal [SceneTransitionLayout]. */ val transitionState: StateFlow<ObservableTransitionState> /** Updates the requested scene. */ - fun setDesiredScene(desiredScene: SceneKey) + fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null) /** * Updates the transition state of the hub [SceneTransitionLayout]. @@ -63,12 +62,10 @@ class CommunalRepositoryImpl @Inject constructor( @Background backgroundScope: CoroutineScope, - sceneContainerFlags: SceneContainerFlags, - sceneContainerRepository: SceneContainerRepository, + @Communal private val sceneDataSource: SceneDataSource, ) : CommunalRepository { - private val _desiredScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default) - override val desiredScene: StateFlow<SceneKey> = _desiredScene.asStateFlow() + override val currentScene: StateFlow<SceneKey> = sceneDataSource.currentScene private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null) @@ -81,8 +78,8 @@ constructor( initialValue = defaultTransitionState, ) - override fun setDesiredScene(desiredScene: SceneKey) { - _desiredScene.value = desiredScene + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { + sceneDataSource.changeScene(toScene, transitionKey) } /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index ada984db9a39..86b254b910b8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -25,6 +25,7 @@ import android.os.UserManager import android.provider.Settings import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.communal.data.repository.CommunalMediaRepository import com.android.systemui.communal.data.repository.CommunalPrefsRepository @@ -142,13 +143,12 @@ constructor( ) /** - * Target scene as requested by the underlying [SceneTransitionLayout] or through - * [onSceneChanged]. + * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene]. * * If [isCommunalAvailable] is false, will return [CommunalScenes.Blank] */ val desiredScene: Flow<SceneKey> = - communalRepository.desiredScene.combine(isCommunalAvailable) { scene, available -> + communalRepository.currentScene.combine(isCommunalAvailable) { scene, available -> if (available) scene else CommunalScenes.Blank } @@ -254,9 +254,12 @@ constructor( !(it is ObservableTransitionState.Idle && it.scene == CommunalScenes.Blank) } - /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */ - fun onSceneChanged(newScene: SceneKey) { - communalRepository.setDesiredScene(newScene) + /** + * Asks for an asynchronous scene witch to [newScene], which will use the corresponding + * installed transition or the one specified by [transitionKey], if provided. + */ + fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) { + communalRepository.changeScene(newScene, transitionKey) } fun setEditModeOpen(isOpen: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 531f19874b2a..095222a4b47a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -49,8 +49,8 @@ abstract class BaseCommunalViewModel( communalInteractor.signalUserInteraction() } - fun onSceneChanged(scene: SceneKey) { - communalInteractor.onSceneChanged(scene) + fun changeScene(scene: SceneKey) { + communalInteractor.changeScene(scene) } /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 902133ddce1d..5f4b394a05b9 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.launch import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.theme.PlatformTheme import com.android.internal.logging.UiEventLogger @@ -150,7 +149,7 @@ constructor( private fun onEditDone() { try { - communalViewModel.onSceneChanged(CommunalScenes.Communal) + communalViewModel.changeScene(CommunalScenes.Communal) checkNotNull(windowManagerService).lockNow(/* options */ null) finish() } catch (e: RemoteException) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 19af371d1dfa..1ed4b503b43d 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -49,6 +49,7 @@ import com.android.systemui.common.data.CommonDataLayerModule; import com.android.systemui.communal.dagger.CommunalModule; import com.android.systemui.complication.dagger.ComplicationComponent; import com.android.systemui.controls.dagger.ControlsModule; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -89,6 +90,7 @@ import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.recordissue.RecordIssueModule; import com.android.systemui.retail.dagger.RetailModeModule; +import com.android.systemui.scene.shared.model.SceneContainerConfig; import com.android.systemui.scene.shared.model.SceneDataSource; import com.android.systemui.scene.shared.model.SceneDataSourceDelegator; import com.android.systemui.scene.ui.view.WindowRootViewComponent; @@ -165,6 +167,8 @@ import java.util.concurrent.Executor; import javax.inject.Named; +import kotlinx.coroutines.CoroutineScope; + /** * A dagger module for injecting components of System UI that are required by System UI. * @@ -402,6 +406,13 @@ public abstract class SystemUIModule { @ClassKey(SystemUISecondaryUserService.class) abstract Service bindsSystemUISecondaryUserService(SystemUISecondaryUserService service); + @Provides + @SysUISingleton + static SceneDataSourceDelegator providesSceneDataSourceDelegator( + @Application CoroutineScope applicationScope, SceneContainerConfig config) { + return new SceneDataSourceDelegator(applicationScope, config); + } + @Binds abstract SceneDataSource bindSceneDataSource(SceneDataSourceDelegator delegator); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt index 1b832d4ab98d..037c23b579c3 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt @@ -57,7 +57,7 @@ constructor( !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId) if (showGlanceableHub) { toGlanceableHubTransitionViewModel.startTransition() - communalInteractor.onSceneChanged(CommunalScenes.Communal) + communalInteractor.changeScene(CommunalScenes.Communal) } else { toLockscreenTransitionViewModel.startTransition() } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt index 69dce83b7136..2fbcba977a91 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt @@ -20,9 +20,6 @@ package com.android.systemui.scene.shared.model import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -36,14 +33,10 @@ import kotlinx.coroutines.flow.stateIn * Delegates calls to a runtime-provided [SceneDataSource] or to a no-op implementation if a * delegate isn't set. */ -@SysUISingleton -class SceneDataSourceDelegator -@Inject -constructor( - @Application private val applicationScope: CoroutineScope, +class SceneDataSourceDelegator( + applicationScope: CoroutineScope, config: SceneContainerConfig, ) : SceneDataSource { - private val noOpDelegate = NoOpSceneDataSource(config.initialSceneKey) private val delegateMutable = MutableStateFlow<SceneDataSource>(noOpDelegate) @@ -82,6 +75,7 @@ constructor( ) : SceneDataSource { override val currentScene: StateFlow<SceneKey> = MutableStateFlow(initialSceneKey).asStateFlow() + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 3169e9ccbbcb..33cf9ba4fa7c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -26,12 +26,14 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.communal.dagger.Communal import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.ui.compose.CommunalContainer import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.phone.SystemUIDialogFactory import com.android.systemui.util.kotlin.collectFlow @@ -52,6 +54,7 @@ constructor( private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val shadeInteractor: ShadeInteractor, private val powerManager: PowerManager, + @Communal private val dataSourceDelegator: SceneDataSourceDelegator, ) { /** The container view for the hub. This will not be initialized until [initView] is called. */ private var communalContainerView: View? = null @@ -125,6 +128,7 @@ constructor( PlatformTheme { CommunalContainer( viewModel = communalViewModel, + dataSourceDelegator = dataSourceDelegator, dialogFactory = dialogFactory, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 07d93508228e..5ca6cf12ba90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -41,6 +41,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.sceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.phone.SystemUIDialogFactory import com.android.systemui.testKosmos @@ -104,7 +105,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { dialogFactory, keyguardTransitionInteractor, shadeInteractor, - powerManager + powerManager, + kosmos.sceneDataSourceDelegator, ) testableLooper = TestableLooper.get(this) @@ -145,6 +147,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { keyguardTransitionInteractor, shadeInteractor, powerManager, + kosmos.sceneDataSourceDelegator, ) // First call succeeds. @@ -268,7 +271,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } private fun goToScene(scene: SceneKey) { - communalRepository.setDesiredScene(scene) + communalRepository.changeScene(scene) testableLooper.processAllMessages() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt index bc0bf9dd069f..de7b14d1e102 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt @@ -32,6 +32,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor import com.android.systemui.scene.SceneContainerFrameworkModule import com.android.systemui.scene.shared.flag.SceneContainerFlags +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSource import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.BaseShadeInteractor @@ -45,6 +46,7 @@ import dagger.Provides import javax.inject.Provider import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -71,7 +73,10 @@ interface SysUITestModule { @Binds @Main fun bindMainResources(resources: Resources): Resources @Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor - @Binds fun bindSceneDataSource(delegator: SceneDataSourceDelegator): SceneDataSource + + @Binds + @SysUISingleton + fun bindSceneDataSource(delegator: SceneDataSourceDelegator): SceneDataSource @Binds fun provideFaceAuthInteractor( @@ -109,6 +114,15 @@ interface SysUITestModule { sceneContainerOff.get() } } + + @Provides + @SysUISingleton + fun providesSceneDataSourceDelegator( + @Application applicationScope: CoroutineScope, + config: SceneContainerConfig, + ): SceneDataSourceDelegator { + return SceneDataSourceDelegator(applicationScope, config) + } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt index 5ff588f810bd..9f5c6b8faa38 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt @@ -2,6 +2,7 @@ package com.android.systemui.communal.data.repository import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.shared.model.CommunalScenes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -17,11 +18,11 @@ import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalRepository( applicationScope: CoroutineScope, - override val desiredScene: MutableStateFlow<SceneKey> = + override val currentScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default), ) : CommunalRepository { - override fun setDesiredScene(desiredScene: SceneKey) { - this.desiredScene.value = desiredScene + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { + this.currentScene.value = toScene } private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) |