diff options
6 files changed, 164 insertions, 9 deletions
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 cf145471e55f..0de036988337 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -27,6 +27,9 @@ import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dock.dockManager import com.android.systemui.dock.fakeDockManager +import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED +import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor @@ -66,6 +69,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fun setUp() { with(kosmos) { fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT) + kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) underTest = CommunalSceneStartable( @@ -76,6 +80,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { keyguardInteractor = keyguardInteractor, systemSettings = fakeSettings, notificationShadeWindowController = notificationShadeWindowController, + featureFlagsClassic = kosmos.fakeFeatureFlagsClassic, applicationScope = applicationCoroutineScope, bgScope = applicationCoroutineScope, mainDispatcher = testDispatcher, @@ -451,6 +456,24 @@ class CommunalSceneStartableTest : SysuiTestCase() { } } + @Test + fun transitionFromDozingToGlanceableHub_forcesCommunal() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalSceneInteractor.currentScene) + communalSceneInteractor.changeScene(CommunalScenes.Blank) + assertThat(scene).isEqualTo(CommunalScenes.Blank) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.GLANCEABLE_HUB, + testScope = this + ) + + assertThat(scene).isEqualTo(CommunalScenes.Communal) + } + } + private fun TestScope.updateDocked(docked: Boolean) = with(kosmos) { runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt index 612f2e73e4bb..ec4fd79b399a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -34,12 +34,14 @@ package com.android.systemui.keyguard.domain.interactor import android.os.PowerManager 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.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository +import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -64,8 +66,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.reset import org.mockito.Mockito.spy +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -120,6 +124,66 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToLockscreen_onPowerButtonPress_canDream_glanceableHubAvailable() = + testScope.runTest { + whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true) + kosmos.setCommunalAvailable(true) + runCurrent() + + powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON) + runCurrent() + + // If dreaming is possible and communal is available, then we should transition to + // GLANCEABLE_HUB when waking up due to power button press. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.GLANCEABLE_HUB, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToLockscreen_onPowerButtonPress_canNotDream_glanceableHubAvailable() = + testScope.runTest { + whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false) + kosmos.setCommunalAvailable(true) + runCurrent() + + powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON) + runCurrent() + + // If dreaming is NOT possible but communal is available, then we should transition to + // LOCKSCREEN when waking up due to power button press. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToLockscreen_onPowerButtonPress_canNDream_glanceableHubNotAvailable() = + testScope.runTest { + whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true) + kosmos.setCommunalAvailable(false) + runCurrent() + + powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON) + runCurrent() + + // If dreaming is possible but communal is NOT available, then we should transition to + // LOCKSCREEN when waking up due to power button press. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() = testScope.runTest { kosmos.fakeCommunalSceneRepository.setTransitionState( diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 88c3f9f6af2e..bde6f42b16af 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -18,7 +18,9 @@ package com.android.systemui.communal import android.provider.Settings import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.TransitionKey import com.android.systemui.CoreStartable +import com.android.systemui.Flags.communalHub import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.shared.model.CommunalScenes @@ -28,6 +30,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dock.DockManager +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -74,6 +78,7 @@ constructor( private val systemSettings: SystemSettings, centralSurfacesOpt: Optional<CentralSurfaces>, private val notificationShadeWindowController: NotificationShadeWindowController, + private val featureFlagsClassic: FeatureFlagsClassic, @Application private val applicationScope: CoroutineScope, @Background private val bgScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, @@ -86,13 +91,21 @@ constructor( private val centralSurfaces: CentralSurfaces? by centralSurfacesOpt + private val flagEnabled: Boolean by lazy { + featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() + } + override fun start() { + if (!flagEnabled) { + return + } + // Handle automatically switching based on keyguard state. keyguardTransitionInteractor.startedKeyguardTransitionStep .mapLatest(::determineSceneAfterTransition) .filterNotNull() - .onEach { nextScene -> - communalSceneInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade) + .onEach { (nextScene, nextTransition) -> + communalSceneInteractor.changeScene(nextScene, nextTransition) } .launchIn(applicationScope) @@ -188,7 +201,7 @@ constructor( private suspend fun determineSceneAfterTransition( lastStartedTransition: TransitionStep, - ): SceneKey? { + ): Pair<SceneKey, TransitionKey>? { val to = lastStartedTransition.to val from = lastStartedTransition.from val docked = dockManager.isDocked @@ -201,22 +214,27 @@ constructor( // underneath the hub is shown. When launching activities over lockscreen, we only // change scenes once the activity launch animation is finished, so avoid // changing the scene here. - CommunalScenes.Blank + Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade) } to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> { // When transitioning to the hub from an occluded state, fade out the hub without // doing any translation. - CommunalScenes.Communal + Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade) } // Transitioning to Blank scene when entering the edit mode will be handled separately // with custom animations. to == KeyguardState.GONE && !communalInteractor.editModeOpen.value -> - CommunalScenes.Blank + Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade) !docked && !KeyguardState.deviceIsAwakeInState(to) -> { // If the user taps the screen and wakes the device within this timeout, we don't // want to dismiss the hub delay(AWAKE_DEBOUNCE_DELAY) - CommunalScenes.Blank + Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade) + } + from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> { + // Make sure the communal hub is showing (immediately, not fading in) when + // transitioning from dozing to hub. + Pair(CommunalScenes.Communal, CommunalTransitionKeys.Immediately) } else -> null } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index aee65a81880d..cd28bec938b8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -17,8 +17,11 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.app.DreamManager import com.android.app.animation.Interpolators import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -28,6 +31,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.util.kotlin.Utils.Companion.sample import com.android.systemui.util.kotlin.sample @@ -53,9 +57,11 @@ constructor( keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, + private val communalSceneInteractor: CommunalSceneInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, val deviceEntryRepository: DeviceEntryRepository, private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor, + private val dreamManager: DreamManager, ) : TransitionInteractor( fromState = KeyguardState.DOZING, @@ -115,6 +121,7 @@ constructor( } } + @SuppressLint("MissingPermission") private fun listenForDozingToAny() { if (KeyguardWmStateRefactor.isEnabled) { return @@ -126,7 +133,8 @@ constructor( .filterRelevantKeyguardStateAnd { isAwake -> isAwake } .sample( keyguardInteractor.isKeyguardOccluded, - communalInteractor.isIdleOnCommunal, + communalInteractor.isCommunalAvailable, + communalSceneInteractor.isIdleOnCommunal, canTransitionToGoneOnWake, keyguardInteractor.primaryBouncerShowing, ) @@ -134,6 +142,7 @@ constructor( ( _, occluded, + isCommunalAvailable, isIdleOnCommunal, canTransitionToGoneOnWake, primaryBouncerShowing) -> @@ -163,6 +172,19 @@ constructor( } else { startTransitionTo(KeyguardState.GLANCEABLE_HUB) } + } else if ( + powerInteractor.detailedWakefulness.value.lastWakeReason == + WakeSleepReason.POWER_BUTTON && + isCommunalAvailable && + dreamManager.canStartDreaming(true) + ) { + // This case handles tapping the power button to transition through + // dream -> off -> hub. + if (SceneContainerFlag.isEnabled) { + // TODO(b/336576536): Check if adaptation for scene framework is needed + } else { + startTransitionTo(KeyguardState.GLANCEABLE_HUB) + } } else { startTransitionTo(KeyguardState.LOCKSCREEN) } @@ -171,6 +193,7 @@ constructor( } /** Figure out what state to transition to when we awake from DOZING. */ + @SuppressLint("MissingPermission") private fun listenForWakeFromDozing() { if (!KeyguardWmStateRefactor.isEnabled) { return @@ -180,7 +203,8 @@ constructor( powerInteractor.detailedWakefulness .filterRelevantKeyguardStateAnd { it.isAwake() } .sample( - communalInteractor.isIdleOnCommunal, + communalInteractor.isCommunalAvailable, + communalSceneInteractor.isIdleOnCommunal, keyguardInteractor.biometricUnlockState, wakeToGoneInteractor.canWakeDirectlyToGone, keyguardInteractor.primaryBouncerShowing, @@ -188,6 +212,7 @@ constructor( .collect { ( _, + isCommunalAvailable, isIdleOnCommunal, biometricUnlockState, canWakeDirectlyToGone, @@ -227,6 +252,23 @@ constructor( ownerReason = "waking from dozing" ) } + } else if ( + powerInteractor.detailedWakefulness.value.lastWakeReason == + WakeSleepReason.POWER_BUTTON && + isCommunalAvailable && + dreamManager.canStartDreaming(true) + ) { + // This case handles tapping the power button to transition through + // dream -> off -> hub. + if (SceneContainerFlag.isEnabled) { + // TODO(b/336576536): Check if adaptation for scene framework is + // needed + } else { + startTransitionTo( + KeyguardState.GLANCEABLE_HUB, + ownerReason = "waking from dozing" + ) + } } else { startTransitionTo( KeyguardState.LOCKSCREEN, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt index 7c53639a85a6..0f8833cfe9f7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt @@ -16,6 +16,7 @@ package com.android.systemui import android.app.ActivityManager +import android.app.DreamManager import android.app.admin.DevicePolicyManager import android.app.trust.TrustManager import android.hardware.fingerprint.FingerprintManager @@ -33,6 +34,7 @@ import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.biometrics.AuthController import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.demomode.DemoModeController import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.ScreenLifecycle @@ -94,6 +96,7 @@ data class TestMocksModule( @get:Provides val demoModeController: DemoModeController = mock(), @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(), @get:Provides val dozeParameters: DozeParameters = mock(), + @get:Provides val dreamManager: DreamManager = mock(), @get:Provides val dumpManager: DumpManager = mock(), @get:Provides val fingerprintManager: FingerprintManager = mock(), @get:Provides val headsUpManager: HeadsUpManager = mock(), @@ -132,6 +135,7 @@ data class TestMocksModule( @get:Provides val systemUIDialogManager: SystemUIDialogManager = mock(), @get:Provides val deviceEntryIconTransitions: Set<DeviceEntryIconTransition> = emptySet(), @get:Provides val communalInteractor: CommunalInteractor = mock(), + @get:Provides val communalSceneInteractor: CommunalSceneInteractor = mock(), @get:Provides val sceneLogger: SceneLogger = mock(), @get:Provides val trustManager: TrustManager = mock(), @get:Provides val primaryBouncerInteractor: PrimaryBouncerInteractor = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt index 446652c7c6d8..126d85890531 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt @@ -16,7 +16,9 @@ package com.android.systemui.keyguard.domain.interactor +import android.service.dream.dreamManager import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.deviceentry.data.repository.deviceEntryRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos @@ -36,9 +38,11 @@ var Kosmos.fromDozingTransitionInteractor by mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, communalInteractor = communalInteractor, + communalSceneInteractor = communalSceneInteractor, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, deviceEntryRepository = deviceEntryRepository, wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor, + dreamManager = dreamManager ) } |