summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author William Xiao <wxyz@google.com> 2024-11-13 23:03:08 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-13 23:03:08 +0000
commit0df244d34c310967c22caea243354dcb5c5cac7a (patch)
tree10c26611b05372c70cd9a5442909bc31c0a63f4b
parent8ab156165dbe5a3b32ae66bbe6f8e6a6f00c78d1 (diff)
parent8d1586605579d26087fb68e4087964a125c31591 (diff)
Merge "Add dream scene transitions for Flexiglass" into main
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt125
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt2
12 files changed, 312 insertions, 11 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index d546a5db495e..9de7a5d659ae 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -16,6 +16,7 @@ import com.android.systemui.scene.ui.composable.transitions.bouncerToLockscreenP
import com.android.systemui.scene.ui.composable.transitions.communalToBouncerTransition
import com.android.systemui.scene.ui.composable.transitions.communalToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.dreamToBouncerTransition
+import com.android.systemui.scene.ui.composable.transitions.dreamToCommunalTransition
import com.android.systemui.scene.ui.composable.transitions.dreamToGoneTransition
import com.android.systemui.scene.ui.composable.transitions.dreamToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
@@ -58,6 +59,7 @@ val SceneContainerTransitions = transitions {
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
from(Scenes.Dream, to = Scenes.Bouncer) { dreamToBouncerTransition() }
+ from(Scenes.Dream, to = Scenes.Communal) { dreamToCommunalTransition() }
from(Scenes.Dream, to = Scenes.Gone) { dreamToGoneTransition() }
from(Scenes.Dream, to = Scenes.Shade) { dreamToShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt
new file mode 100644
index 000000000000..93c10b6224ab
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromDreamToCommunalTransition.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.tween
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.communal.ui.compose.AllElements
+import com.android.systemui.communal.ui.compose.Communal
+
+fun TransitionBuilder.dreamToCommunalTransition() {
+ spec = tween(durationMillis = 1000)
+
+ // Translate communal hub grid from the end direction.
+ translate(Communal.Elements.Grid, Edge.End)
+
+ // Fade all communal hub elements.
+ timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 8062358f670c..a65e7ed48797 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -19,7 +19,9 @@ import android.app.WindowConfiguration
import android.content.ComponentName
import android.content.Intent
import android.os.RemoteException
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.service.dreams.Flags
import android.service.dreams.IDreamOverlay
import android.service.dreams.IDreamOverlayCallback
@@ -33,7 +35,6 @@ import android.view.WindowManagerImpl
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCapture
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
@@ -43,6 +44,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
@@ -62,12 +64,16 @@ import com.android.systemui.complication.ComplicationLayoutEngine
import com.android.systemui.complication.dagger.ComplicationComponent
import com.android.systemui.dreams.complication.HideComplicationTouchHandler
import com.android.systemui.dreams.dagger.DreamOverlayComponent
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.gesture.domain.gestureInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
import com.android.systemui.navigationbar.gestural.domain.TaskInfo
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.touch.TouchInsetManager
import com.android.systemui.util.concurrency.FakeExecutor
@@ -98,12 +104,14 @@ import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class DreamOverlayServiceTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val mFakeSystemClock = FakeSystemClock()
private val mMainExecutor = FakeExecutor(mFakeSystemClock)
private val kosmos = testKosmos()
@@ -245,6 +253,10 @@ class DreamOverlayServiceTest : SysuiTestCase() {
)
}
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -287,6 +299,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
mKeyguardUpdateMonitor,
mScrimManager,
mCommunalInteractor,
+ kosmos.sceneInteractor,
mSystemDialogsCloser,
mUiEventLogger,
mTouchInsetManager,
@@ -768,6 +781,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_SCENE_CONTAINER)
@kotlin.Throws(RemoteException::class)
fun testTransitionToGlanceableHub() =
testScope.runTest {
@@ -793,6 +807,35 @@ class DreamOverlayServiceTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_SCENE_CONTAINER, FLAG_COMMUNAL_HUB)
+ @kotlin.Throws(RemoteException::class)
+ fun testTransitionToGlanceableHub_sceneContainer() =
+ testScope.runTest {
+ // Inform the overlay service of dream starting. Do not show dream complications.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*isPreview*/,
+ false, /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ verify(mDreamOverlayCallback).onRedirectWake(false)
+ clearInvocations(mDreamOverlayCallback)
+ kosmos.setCommunalAvailable(true)
+ mMainExecutor.runAllReady()
+ runCurrent()
+ verify(mDreamOverlayCallback).onRedirectWake(true)
+ client.onWakeRequested()
+ mMainExecutor.runAllReady()
+ runCurrent()
+ assertThat(kosmos.sceneContainerRepository.currentScene.value)
+ .isEqualTo(Scenes.Communal)
+ verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
@Throws(RemoteException::class)
fun testRedirectExit() =
@@ -911,6 +954,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
// Verifies that the touch handling lifecycle is STARTED even if the dream starts while not
// focused.
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testLifecycle_dreamNotFocusedOnStart_isStarted() {
val transitionState: MutableStateFlow<ObservableTransitionState> =
MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Blank))
@@ -1024,6 +1068,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testCommunalVisible_setsLifecycleState() {
val client = client
@@ -1060,6 +1105,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
// Verifies the dream's lifecycle
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testLifecycleStarted_whenAnyOcclusion() {
val client = client
@@ -1256,5 +1302,11 @@ class DreamOverlayServiceTest : SysuiTestCase() {
ComponentName("package", "homeControlPanel")
private const val DREAM_COMPONENT = "package/dream"
private const val WINDOW_NAME = "test"
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_HUB).andSceneContainer()
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
index bf97afed92f4..959081663b56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
@@ -27,6 +27,7 @@ import com.android.compose.animation.scene.ObservableTransitionState.Transition.
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -194,6 +195,24 @@ class SceneContainerOcclusionInteractorTest : SysuiTestCase() {
.isFalse()
}
+ @Test
+ fun invisibleDueToOcclusion_isDreaming_emitsTrue() =
+ testScope.runTest {
+ val invisibleDueToOcclusion by collectLastValue(underTest.invisibleDueToOcclusion)
+
+ // Verify that we start with unoccluded
+ assertWithMessage("Should start unoccluded").that(invisibleDueToOcclusion).isFalse()
+
+ // Start dreaming, which is an occluding activity
+ showOccludingActivity()
+ kosmos.keyguardInteractor.setDreaming(true)
+
+ // Verify not invisible when dreaming
+ assertWithMessage("Should be invisible when dreaming")
+ .that(invisibleDueToOcclusion)
+ .isTrue()
+ }
+
/** Simulates the appearance of a show-when-locked `Activity` in the foreground. */
private fun TestScope.showOccludingActivity() {
keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index af0274b1caaa..2e074da02103 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -73,6 +73,7 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.dozeInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
@@ -143,6 +144,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val deviceEntryHapticsInteractor by lazy { kosmos.deviceEntryHapticsInteractor }
+ private val dozeInteractor by lazy { kosmos.dozeInteractor }
+ private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val sceneBackInteractor by lazy { kosmos.sceneBackInteractor }
private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
@@ -373,6 +376,64 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun hydrateVisibility_whileDreaming() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ // GIVEN the device is dreaming
+ val transitionState =
+ prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Dream)
+ underTest.start()
+ assertThat(isVisible).isFalse()
+ }
+
+ @Test
+ fun hydrateVisibility_onCommunalWhileOccluded() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ true,
+ mock(),
+ )
+ prepareState(isDeviceUnlocked = false, initialSceneKey = Scenes.Communal)
+ underTest.start()
+ runCurrent()
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
+ fun hydrateVisibility_inCommunalTransition() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+
+ // GIVEN the device is dreaming
+ val transitionState =
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Dream,
+ )
+ underTest.start()
+ assertThat(isVisible).isFalse()
+
+ // WHEN a transition starts to the communal hub
+ sceneInteractor.changeScene(Scenes.Dream, "switching to dream for test")
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Dream,
+ toScene = Scenes.Communal,
+ currentScene = flowOf(Scenes.Dream),
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ // THEN scenes are visible
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
fun startsInLockscreenScene() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -643,7 +704,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() =
testScope.runTest {
kosmos.lockscreenSceneTransitionInteractor.start()
- val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+ val asleepState by collectLastValue(keyguardInteractor.asleepKeyguardState)
val currentTransitionInfo by
collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
val transitionState =
@@ -673,7 +734,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() =
testScope.runTest {
kosmos.lockscreenSceneTransitionInteractor.start()
- val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
+ val asleepState by collectLastValue(keyguardInteractor.asleepKeyguardState)
val currentTransitionInfo by
collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
val transitionState =
@@ -2360,6 +2421,66 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun stayOnLockscreen_whenDozingStarted() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ // Stay on Lockscreen when dozing and dreaming
+ dozeInteractor.setIsDozing(true)
+ keyguardInteractor.setDreaming(true)
+ kosmos.fakeKeyguardRepository.setDreamingWithOverlay(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun switchFromLockscreenToDream_whenDreamStarted() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ powerInteractor.setAwakeForTest()
+ keyguardInteractor.setDreaming(true)
+ // Move past initial delay with [KeyguardInteractor#isAbleToDream]
+ advanceTimeBy(600L)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ }
+
+ @Test
+ fun switchFromDreamToLockscreen_whenLockedAndDreamStopped() =
+ testScope.runTest {
+ keyguardInteractor.setDreaming(true)
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(initialSceneKey = Scenes.Dream)
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ underTest.start()
+
+ keyguardInteractor.setDreaming(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun switchFromDreamToGone_whenUnlockedAndDreamStopped() =
+ testScope.runTest {
+ keyguardInteractor.setDreaming(true)
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(initialSceneKey = Scenes.Dream, isDeviceUnlocked = true)
+ assertThat(currentScene).isEqualTo(Scenes.Dream)
+ underTest.start()
+
+ keyguardInteractor.setDreaming(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
testScope.runTest {
val transitionState =
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 1ffbbd2a9f32..b330ba376810 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,9 @@ import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -162,6 +165,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private TouchMonitor mTouchMonitor;
+ private final SceneInteractor mSceneInteractor;
private final CommunalInteractor mCommunalInteractor;
private boolean mCommunalAvailable;
@@ -378,6 +382,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
KeyguardUpdateMonitor keyguardUpdateMonitor,
ScrimManager scrimManager,
CommunalInteractor communalInteractor,
+ SceneInteractor sceneInteractor,
SystemDialogsCloser systemDialogsCloser,
UiEventLogger uiEventLogger,
@Named(DREAM_TOUCH_INSET_MANAGER) TouchInsetManager touchInsetManager,
@@ -405,6 +410,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mDreamOverlayCallbackController = dreamOverlayCallbackController;
mWindowTitle = windowTitle;
mCommunalInteractor = communalInteractor;
+ mSceneInteractor = sceneInteractor;
mSystemDialogsCloser = systemDialogsCloser;
mGestureInteractor = gestureInteractor;
mDreamOverlayComponentFactory = dreamOverlayComponentFactory;
@@ -551,9 +557,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Override
public void onWakeRequested() {
mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START);
- mCommunalInteractor.changeScene(CommunalScenes.Communal,
- "dream wake requested",
- null);
+ if (SceneContainerFlag.isEnabled()) {
+ // Scene interactor can only be modified on main thread.
+ mExecutor.execute(() -> mSceneInteractor.changeScene(Scenes.Communal,
+ "dream wake redirect to communal"));
+ } else {
+ mCommunalInteractor.changeScene(CommunalScenes.Communal,
+ "dream wake requested",
+ null);
+ }
}
private void updateGestureBlockingLocked() {
@@ -617,7 +629,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mSystemDialogsCloser.closeSystemDialogs();
// Hide glanceable hub (this is a nop if glanceable hub is not open).
- mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
+ if (SceneContainerFlag.isEnabled()) {
+ // Scene interactor can only be modified on main thread.
+ mExecutor.execute(
+ () -> mSceneInteractor.changeScene(Scenes.Dream, "closing hub to go to dream"));
+ } else {
+ mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
index b8ac0d282229..5a9e52ae5655 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt
@@ -54,6 +54,8 @@ constructor(
shadeInteractor.shadeMode,
) { isDeviceUnlocked, shadeMode ->
buildList {
+ add(Swipe.Start to Scenes.Communal)
+
val bouncerOrGone =
if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer
add(Swipe.Up to bouncerOrGone)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 667827ac4724..c96ea03f057a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -138,6 +138,7 @@ constructor(
Overlays.QuickSettingsShade -> false
Scenes.Bouncer -> false
Scenes.Communal -> true
+ Scenes.Dream -> false
Scenes.Gone -> true
Scenes.Lockscreen -> true
Scenes.QuickSettings -> false
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 41a3c8aff6cf..b89eb5c762e0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -23,6 +23,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
@@ -46,6 +47,7 @@ class HomeSceneFamilyResolver
constructor(
@Application private val applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
+ keyguardInteractor: KeyguardInteractor,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.Home
@@ -56,6 +58,7 @@ constructor(
deviceEntryInteractor.canSwipeToEnter,
deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
+ keyguardInteractor.isDreamingWithOverlay,
transform = ::homeScene,
)
.stateIn(
@@ -67,7 +70,8 @@ constructor(
canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value,
isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
isUnlocked = deviceEntryInteractor.isUnlocked.value,
- )
+ isDreamingWithOverlay = false,
+ ),
)
override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
@@ -77,8 +81,11 @@ constructor(
canSwipeToEnter: Boolean?,
isDeviceEntered: Boolean,
isUnlocked: Boolean,
+ isDreamingWithOverlay: Boolean,
): SceneKey =
when {
+ // Dream can run even if Keyguard is disabled, thus it has the highest priority here.
+ isDreamingWithOverlay -> Scenes.Dream
!isKeyguardEnabled -> Scenes.Gone
canSwipeToEnter == true -> Scenes.Lockscreen
!isDeviceEntered -> Scenes.Lockscreen
@@ -91,6 +98,9 @@ constructor(
setOf(
Scenes.Gone,
Scenes.Lockscreen,
+ // Dream is a home scene as the dream activity occludes keyguard and can show the
+ // shade on top.
+ Scenes.Dream,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index daeaaa52fd94..9125d7e8bb0e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -62,6 +62,7 @@ import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -217,7 +218,9 @@ constructor(
sceneInteractor.transitionState.mapNotNull { state ->
when (state) {
is ObservableTransitionState.Idle -> {
- if (state.currentScene != Scenes.Gone) {
+ if (state.currentScene == Scenes.Dream) {
+ false to "dream is showing"
+ } else if (state.currentScene != Scenes.Gone) {
true to "scene is not Gone"
} else if (state.currentOverlays.isNotEmpty()) {
true to "overlay is shown"
@@ -228,21 +231,30 @@ constructor(
is ObservableTransitionState.Transition -> {
if (state.fromContent == Scenes.Gone) {
true to "scene transitioning away from Gone"
+ } else if (state.fromContent == Scenes.Dream) {
+ true to "scene transitioning away from dream"
} else {
null
}
}
}
},
+ sceneInteractor.transitionState.map { state ->
+ state.isTransitioningFromOrTo(Scenes.Communal) ||
+ state.isIdle(Scenes.Communal)
+ },
headsUpInteractor.isHeadsUpOrAnimatingAway,
occlusionInteractor.invisibleDueToOcclusion,
alternateBouncerInteractor.isVisible,
) {
visibilityForTransitionState,
+ isCommunalShowing,
isHeadsUpOrAnimatingAway,
invisibleDueToOcclusion,
isAlternateBouncerVisible ->
when {
+ isCommunalShowing ->
+ true to "on or transitioning to/from communal"
isHeadsUpOrAnimatingAway -> true to "showing a HUN"
isAlternateBouncerVisible -> true to "showing alternate bouncer"
invisibleDueToOcclusion -> false to "invisible due to occlusion"
@@ -266,6 +278,7 @@ constructor(
handleSimUnlock()
handleDeviceUnlockStatus()
handlePowerState()
+ handleDreamState()
handleShadeTouchability()
}
@@ -506,6 +519,31 @@ constructor(
}
}
+ private fun handleDreamState() {
+ applicationScope.launch {
+ keyguardInteractor.isAbleToDream
+ .sample(sceneInteractor.transitionState, ::Pair)
+ .collect { (isAbleToDream, transitionState) ->
+ if (transitionState.isIdle(Scenes.Communal)) {
+ // The dream is automatically started underneath the hub, don't transition
+ // to dream when this is happening as communal is still visible on top.
+ return@collect
+ }
+ if (isAbleToDream) {
+ switchToScene(
+ targetSceneKey = Scenes.Dream,
+ loggingReason = "dream started",
+ )
+ } else {
+ switchToScene(
+ targetSceneKey = SceneFamilies.Home,
+ loggingReason = "dream stopped",
+ )
+ }
+ }
+ }
+ }
+
private fun handleShadeTouchability() {
applicationScope.launch {
shadeInteractor.isShadeTouchable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index da04f6edf9e2..b2ca33a4aecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -705,6 +705,7 @@ public class StatusBarStateControllerImpl implements
final boolean onBouncer = currentScene.equals(Scenes.Bouncer);
final boolean onCommunal = currentScene.equals(Scenes.Communal);
final boolean onGone = currentScene.equals(Scenes.Gone);
+ final boolean onDream = currentScene.equals(Scenes.Dream);
final boolean onLockscreen = currentScene.equals(Scenes.Lockscreen);
final boolean onQuickSettings = currentScene.equals(Scenes.QuickSettings);
final boolean onShade = currentScene.equals(Scenes.Shade);
@@ -765,6 +766,8 @@ public class StatusBarStateControllerImpl implements
// We get here if deviceUnlockStatus.isUnlocked is false but we are no longer on or over
// a keyguardish scene; we want to return SHADE_LOCKED until isUnlocked is also true.
newState = StatusBarState.SHADE_LOCKED;
+ } else if (onDream) {
+ newState = StatusBarState.SHADE_LOCKED;
} else {
throw new IllegalArgumentException(
"unhandled input to calculateStateFromSceneFramework: " + inputLogString);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
index 8b124258909a..a4a63ec6ca21 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -21,6 +21,7 @@ package com.android.systemui.scene.domain.resolver
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -34,6 +35,7 @@ val Kosmos.homeSceneFamilyResolver by
HomeSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
deviceEntryInteractor = deviceEntryInteractor,
+ keyguardInteractor = keyguardInteractor,
keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}