Return to dream from occlusion.
This changelist tracks the dream state when the lockscreen is occluded
and then returns to that state on unocclusion.
Test: atest KeyguardTransitionScenariosTest#dreamingToOccludedToDreaming
Test: atest CommunalDreamStartableTest#CommunalDreamStartableTest#restartDreamingWhenTransitioningFromDreamingToOccludedToDreaming
Fixes: 338051457
Flag: ACONFIG com.android.systemui.restart_dream_on_unocclude disabled
Change-Id: If075685b466aa4a4ccfd77a757a7d715ccf5d3e3
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 80398cd..df44076 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -857,6 +857,16 @@
}
flag {
+ name: "restart_dream_on_unocclude"
+ namespace: "systemui"
+ description: "re-enters dreaming upon unocclude when dreaming when originally occluding"
+ bug: "338051457"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "communal_bouncer_do_not_modify_plugin_open"
namespace: "systemui"
description: "do not modify notification shade when handling bouncer expansion."
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index def63ec..bfed33c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -16,12 +16,16 @@
package com.android.systemui.communal
+import android.platform.test.annotations.EnableFlags
import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -62,6 +66,7 @@
powerInteractor = kosmos.powerInteractor,
keyguardInteractor = kosmos.keyguardInteractor,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+ communalInteractor = kosmos.communalInteractor,
dreamManager = dreamManager,
bgScope = kosmos.applicationCoroutineScope,
)
@@ -84,6 +89,32 @@
}
@Test
+ @EnableFlags(Flags.FLAG_RESTART_DREAM_ON_UNOCCLUDE)
+ fun restartDreamingWhenTransitioningFromDreamingToOccludedToDreaming() =
+ testScope.runTest {
+ keyguardRepository.setDreaming(false)
+ powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
+ whenever(dreamManager.canStartDreaming(/* isScreenOn = */ true)).thenReturn(true)
+ runCurrent()
+
+ verify(dreamManager, never()).startDream()
+
+ kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ runCurrent()
+
+ transition(from = KeyguardState.DREAMING, to = KeyguardState.OCCLUDED)
+ kosmos.fakeKeyguardRepository.setKeyguardOccluded(false)
+ kosmos.fakeKeyguardRepository.setDreaming(false)
+ runCurrent()
+
+ transition(from = KeyguardState.OCCLUDED, to = KeyguardState.DREAMING)
+ runCurrent()
+
+ verify(dreamManager).startDream()
+ }
+
+ @Test
fun shouldNotStartDreamWhenIneligibleToDream() =
testScope.runTest {
keyguardRepository.setDreaming(false)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 9e7fb4e..153b7aa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -20,6 +20,8 @@
import android.app.DreamManager
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.restartDreamOnUnocclude
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -27,8 +29,10 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -43,6 +47,7 @@
private val powerInteractor: PowerInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val communalInteractor: CommunalInteractor,
private val dreamManager: DreamManager,
@Background private val bgScope: CoroutineScope,
) : CoreStartable {
@@ -52,6 +57,19 @@
return
}
+ // Return to dream from occluded when not already dreaming.
+ if (restartDreamOnUnocclude()) {
+ keyguardTransitionInteractor.startedKeyguardTransitionStep
+ .sample(keyguardInteractor.isDreaming, ::Pair)
+ .filter {
+ it.first.from == KeyguardState.OCCLUDED &&
+ it.first.to == KeyguardState.DREAMING &&
+ !it.second
+ }
+ .onEach { dreamManager.startDream() }
+ .launchIn(bgScope)
+ }
+
// Restart the dream underneath the hub in order to support the ability to swipe
// away the hub to enter the dream.
keyguardTransitionInteractor.finishedKeyguardState
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 6b4cf79..06c8396 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
@@ -163,6 +163,13 @@
initialValue = false,
)
+ /** Whether to start dreaming when returning from occluded */
+ val dreamFromOccluded: Flow<Boolean> =
+ keyguardTransitionInteractor
+ .transitionStepsToState(KeyguardState.OCCLUDED)
+ .map { it.from == KeyguardState.DREAMING }
+ .stateIn(scope = applicationScope, SharingStarted.Eagerly, false)
+
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index e51ba83..2a7178f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -18,6 +18,7 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.restartDreamOnUnocclude
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -90,12 +91,15 @@
.sample(
communalInteractor.isIdleOnCommunal,
communalInteractor.showCommunalFromOccluded,
+ communalInteractor.dreamFromOccluded
)
- .collect { (_, isIdleOnCommunal, showCommunalFromOccluded) ->
+ .collect { (_, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded) ->
// Occlusion signals come from the framework, and should interrupt any
// existing transition
val to =
- if (isIdleOnCommunal || showCommunalFromOccluded) {
+ if (restartDreamOnUnocclude() && dreamFromOccluded) {
+ KeyguardState.DREAMING
+ } else if (isIdleOnCommunal || showCommunalFromOccluded) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
@@ -110,15 +114,19 @@
keyguardInteractor.isKeyguardShowing,
communalInteractor.isIdleOnCommunal,
communalInteractor.showCommunalFromOccluded,
+ communalInteractor.dreamFromOccluded,
)
- .filterRelevantKeyguardStateAnd { (isOccluded, isShowing, _, _) ->
+ .filterRelevantKeyguardStateAnd { (isOccluded, isShowing, _, _, _) ->
!isOccluded && isShowing
}
- .collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded) ->
+ .collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded)
+ ->
// Occlusion signals come from the framework, and should interrupt any
// existing transition
val to =
- if (isIdleOnCommunal || showCommunalFromOccluded) {
+ if (restartDreamOnUnocclude() && dreamFromOccluded) {
+ KeyguardState.DREAMING
+ } else if (isIdleOnCommunal || showCommunalFromOccluded) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 687e91a..c3a806b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
@@ -1424,6 +1425,39 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
+ @EnableFlags(Flags.FLAG_RESTART_DREAM_ON_UNOCCLUDE)
+ fun dreamingToOccludedToDreaming() =
+ testScope.runTest {
+ // GIVEN a device on lockscreen
+ keyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // Given a device that is dreaming
+ keyguardRepository.setDreaming(true)
+
+ // GIVEN a prior transition has run to OCCLUDED
+ runTransitionAndSetWakefulness(KeyguardState.DREAMING, KeyguardState.OCCLUDED)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ // WHEN occlusion ends
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ // THEN a transition to GLANCEABLE_HUB should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromOccludedTransitionInteractor::class.simpleName,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.DREAMING,
+ animatorAssertion = { it.isNotNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun dreamingToPrimaryBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING