diff options
18 files changed, 337 insertions, 66 deletions
diff --git a/packages/SystemUI/docs/imgs/ribbon.png b/packages/SystemUI/docs/imgs/ribbon.png Binary files differindex 9f5765232aed..3379d3d95025 100644 --- a/packages/SystemUI/docs/imgs/ribbon.png +++ b/packages/SystemUI/docs/imgs/ribbon.png diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md index 3e4a1b4a05c7..715d16015402 100644 --- a/packages/SystemUI/docs/scene.md +++ b/packages/SystemUI/docs/scene.md @@ -20,14 +20,16 @@ over several dimensions: from one scene to another) are also pulled out and separated from the content of the UI. -In addition to the above, some of the **secondary goals** are: 4. Make -**customization easier**: by separating scenes to standalone pieces, it becomes -possible for variant owners and OEMs to exclude or replace certain scenes or to -add brand-new scenes. 5. **Enable modularization**: by separating scenes to -standalone pieces, it becomes possible to break down System UI into smaller -codebases, each one of which could be built on its own. Note: this isn't part of -the scene framework itself but is something that can be done more easily once -the scene framework is in place. +In addition to the above, some of the **secondary goals** are: + +4. Make **customization easier**: by separating scenes to standalone pieces, it +becomes possible for variant owners and OEMs to exclude or replace certain scenes +or to add brand-new scenes. +5. **Enable modularization**: by separating scenes to standalone pieces, it +becomes possible to break down System UI into smaller codebases, each one of +which could be built on its own. Note: this isn't part of the scene framework +itself but is something that can be done more easily once the scene framework +is in place. ## Terminology @@ -70,15 +72,17 @@ file evalutes to `true`. running: `console $ adb shell statusbar cmd migrate_keyguard_status_bar_view true` 3. Set a collection of **aconfig flags** to `true` by running the following - commands: `console $ adb shell device_config put systemui - com.android.systemui.scene_container true $ adb shell device_config put - systemui com.android.systemui.keyguard_bottom_area_refactor true $ adb shell - device_config put systemui - com.android.systemui.keyguard_shade_migration_nssl true $ adb shell - device_config put systemui com.android.systemui.media_in_scene_container - true` -4. **Restart** System UI by issuing the following command: `console $ adb shell - am crash com.android.systemui` + commands: + ```console + $ adb shell device_config put systemui com.android.systemui.scene_container true + $ adb shell device_config put systemui com.android.systemui.keyguard_bottom_area_refactor true + $ adb shell device_config put systemui com.android.systemui.keyguard_shade_migration_nssl true + $ adb shell device_config put systemui com.android.systemui.media_in_scene_container true + ``` +4. **Restart** System UI by issuing the following command: + ```console + $ adb shell am crash com.android.systemui + ``` 5. **Verify** that the scene framework was turned on. There are two ways to do this: @@ -96,10 +100,15 @@ file evalutes to `true`. # Look for the log statements from the framework: -$ adb logcat -v time SceneFramework:* *:S ``` +```console +$ adb logcat -v time SceneFramework:* *:S +``` + +To **disable** the framework, simply turn off the main aconfig flag: -To **disable** the framework, simply turn off the main aconfig flag: `console $ -adb shell device_config put systemui com.android.systemui.scene_container false` +```console +$ adb shell device_config put systemui com.android.systemui.scene_container false +``` ## Defining a scene @@ -118,28 +127,28 @@ between any two scenes. The Scene Framework has other ways to define how the content of your UI changes with and throughout a transition to learn more please see the [Scene transition animations](#Scene-transition-animations) section -For example: ```kotlin @SysUISingleton class YourScene @Inject constructor( // -your dependencies here ) : ComposableScene { override val key = -SceneKey.YourScene +For example: -``` -override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = - MutableStateFlow<Map<UserAction, SceneModel>>( - mapOf( - // This is where scene navigation is defined, more on that below. - ) - ).asStateFlow() - -@Composable -override fun SceneScope.Content( - modifier: Modifier, -) { - // This is where the UI is defined using Jetpack Compose. +```kotlin +@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : ComposableScene { + override val key = SceneKey.YourScene + + override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> = + MutableStateFlow<Map<UserAction, SceneModel>>( + mapOf( + // This is where scene navigation is defined, more on that below. + ) + ).asStateFlow() + + @Composable + override fun SceneScope.Content( + modifier: Modifier, + ) { + // This is where the UI is defined using Jetpack Compose. + } } ``` -} ``` - ### Injecting scenes Scenes are injected into the Dagger dependency graph from the @@ -200,20 +209,21 @@ fun TransitionBuilder.lockscreenToShadeTransition() { } ``` -Going through the example code: * The `spec` is the animation that should be -invoked, in the example above, we use a `tween` animation with a duration of 500 -milliseconds * Then there's a series of function calls: `punchHole` applies a -clip mask to the `Scrim` element in the destination scene (in this case it's the -`Shade` scene) which has the position and size determined by the `bounds` -parameter and the shape passed into the `shape` parameter. This lets the -`Lockscreen` scene render "through" the `Shade` scene * The `translate` call -shifts the `Scrim` element to/from the `Top` edge of the scene container * The -first `fractionRange` wrapper tells the system to apply its contained functions +Going through the example code: + +* The `spec` is the animation that should be invoked, in the example above, we use a `tween` +animation with a duration of 500 milliseconds +* Then there's a series of function calls: `punchHole` applies a clip mask to the `Scrim` +element in the destination scene (in this case it's the `Shade` scene) which has the +position and size determined by the `bounds` parameter and the shape passed into the `shape` +parameter. This lets the `Lockscreen` scene render "through" the `Shade` scene +* The `translate` call shifts the `Scrim` element to/from the `Top` edge of the scene container +* The first `fractionRange` wrapper tells the system to apply its contained functions only during the first half of the transition. Inside of it, we see a `fade` of the `ScrimBackground` element and a `translate` o the `CollpasedGrid` element -to/from the `Top` edge * The second `fractionRange` only starts at the second -half of the transition (e.g. when the previous one ends) and applies a `fade` on -the `Notifications` element +to/from the `Top` edge +* The second `fractionRange` only starts at the second half of the transition (e.g. when +the previous one ends) and applies a `fade` on the `Notifications` element You can find the actual documentation for this API [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt). diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 2a793ea70292..030d41ddd8fb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -45,6 +45,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.classifier.FalsingA11yDelegate import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -819,6 +820,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { // While listening, going from the bouncer scene to the gone scene, does dismiss the // keyguard. + kosmos.fakeDeviceEntryRepository.setUnlocked(true) + runCurrent() sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index 62d23152b77a..52305b1f5212 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -336,6 +336,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) + runCurrent() underTest.attemptDeviceEntry() @@ -353,6 +354,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) + runCurrent() underTest.attemptDeviceEntry() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt new file mode 100644 index 000000000000..32943a19be28 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt @@ -0,0 +1,115 @@ +/* + * 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.deviceentry.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository +import com.android.systemui.authentication.domain.interactor.authenticationInteractor +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceUnlockedInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val authenticationRepository = kosmos.fakeAuthenticationRepository + private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository + + val underTest = + DeviceUnlockedInteractor( + applicationScope = testScope.backgroundScope, + authenticationInteractor = kosmos.authenticationInteractor, + deviceEntryRepository = deviceEntryRepository, + ) + + @Test + fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsNone_isTrue() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(true) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + assertThat(isUnlocked).isTrue() + } + + @Test + fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsPin_isTrue() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(true) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + + assertThat(isUnlocked).isTrue() + } + + @Test + fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsSim_isFalse() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(true) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim) + + assertThat(isUnlocked).isFalse() + } + + @Test + fun isDeviceUnlocked_whenLockedAndAuthMethodIsNone_isTrue() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(false) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + assertThat(isUnlocked).isTrue() + } + + @Test + fun isDeviceUnlocked_whenLockedAndAuthMethodIsPin_isFalse() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(false) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + + assertThat(isUnlocked).isFalse() + } + + @Test + fun isDeviceUnlocked_whenLockedAndAuthMethodIsSim_isFalse() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isDeviceUnlocked) + + deviceEntryRepository.setUnlocked(false) + authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim) + + assertThat(isUnlocked).isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index d159986015a0..bf99a8687aa4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.scene.data.repository.sceneContainerRepository @@ -33,6 +34,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -69,6 +71,27 @@ class SceneInteractorTest : SysuiTestCase() { } @Test + fun changeScene_toGoneWhenUnl_doesNotThrow() = + testScope.runTest { + val desiredScene by collectLastValue(underTest.desiredScene) + assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) + + kosmos.fakeDeviceEntryRepository.setUnlocked(true) + runCurrent() + + underTest.changeScene(SceneModel(SceneKey.Gone), "reason") + assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test(expected = IllegalStateException::class) + fun changeScene_toGoneWhenStillLocked_throws() = + testScope.runTest { + kosmos.fakeDeviceEntryRepository.setUnlocked(false) + + underTest.changeScene(SceneModel(SceneKey.Gone), "reason") + } + + @Test fun onSceneChanged() = testScope.runTest { val desiredScene by collectLastValue(underTest.desiredScene) 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 4afa5f2a44b9..fc0df1288553 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 @@ -311,6 +311,10 @@ class SceneContainerStartableTest : SysuiTestCase() { SceneKey.QuickSettings, ) .forEachIndexed { index, sceneKey -> + if (sceneKey == SceneKey.Gone) { + kosmos.fakeDeviceEntryRepository.setUnlocked(true) + runCurrent() + } sceneInteractor.changeScene(SceneModel(sceneKey), "reason") runCurrent() verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY) @@ -420,6 +424,8 @@ class SceneContainerStartableTest : SysuiTestCase() { } // Changing to the Gone scene should report a successful unlock. + kosmos.fakeDeviceEntryRepository.setUnlocked(true) + runCurrent() sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason") runCurrent() verify(falsingCollector).onSuccessfulUnlock() @@ -613,6 +619,8 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(falsingCollector).onBouncerShown() + kosmos.fakeDeviceEntryRepository.setUnlocked(true) + runCurrent() sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason") runCurrent() verify(falsingCollector, times(2)).onBouncerHidden() @@ -741,9 +749,15 @@ class SceneContainerStartableTest : SysuiTestCase() { "Lockscreen cannot be disabled while having a secure authentication method" } } + + check(initialSceneKey != SceneKey.Gone || isDeviceUnlocked) { + "Cannot start on the Gone scene and have the device be locked at the same time." + } + sceneContainerFlags.enabled = true kosmos.fakeDeviceEntryRepository.setUnlocked(isDeviceUnlocked) kosmos.fakeDeviceEntryRepository.setBypassEnabled(isBypassEnabled) + runCurrent() val transitionStateFlow = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(SceneKey.Lockscreen) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index e9801652f060..251daffe2d91 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -157,9 +157,11 @@ class ShadeSceneViewModelTest : SysuiTestCase() { testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) + kosmos.fakeDeviceEntryRepository.setUnlocked(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) + runCurrent() sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 2680328d5d7e..09853578d3f1 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -61,6 +61,7 @@ constructor( deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, trustRepository: TrustRepository, flags: SceneContainerFlags, + deviceUnlockedInteractor: DeviceUnlockedInteractor, ) { val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> = repository.enteringDeviceFromBiometricUnlock @@ -74,19 +75,7 @@ constructor( * of this flow will always be `true`, even if the lockscreen is showing and still needs to be * dismissed by the user to proceed. */ - val isUnlocked: StateFlow<Boolean> = - combine( - repository.isUnlocked, - authenticationInteractor.authenticationMethod, - ) { isUnlocked, authenticationMethod -> - (!authenticationMethod.isSecure || isUnlocked) && - authenticationMethod != AuthenticationMethodModel.Sim - } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) + val isUnlocked: StateFlow<Boolean> = deviceUnlockedInteractor.isDeviceUnlocked /** * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt new file mode 100644 index 000000000000..b0495fb8e819 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt @@ -0,0 +1,62 @@ +/* + * 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.deviceentry.domain.interactor + +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class DeviceUnlockedInteractor +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + authenticationInteractor: AuthenticationInteractor, + deviceEntryRepository: DeviceEntryRepository, +) { + + /** + * Whether the device is unlocked. + * + * A device that is not yet unlocked requires unlocking by completing an authentication + * challenge according to the current authentication method, unless in cases when the current + * authentication method is not "secure" (for example, None and Swipe); in such cases, the value + * of this flow will always be `true`, even if the lockscreen is showing and still needs to be + * dismissed by the user to proceed. + */ + val isDeviceUnlocked: StateFlow<Boolean> = + combine( + deviceEntryRepository.isUnlocked, + authenticationInteractor.authenticationMethod, + ) { isUnlocked, authenticationMethod -> + (!authenticationMethod.isSecure || isUnlocked) && + authenticationMethod != AuthenticationMethodModel.Sim + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 0abde4d5c3f4..b3d2e0918db6 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.logger.SceneLogger @@ -50,6 +51,7 @@ constructor( private val repository: SceneContainerRepository, private val powerInteractor: PowerInteractor, private val logger: SceneLogger, + private val deviceUnlockedInteractor: DeviceUnlockedInteractor, ) { /** @@ -222,6 +224,11 @@ constructor( loggingReason: String, log: (from: SceneKey, to: SceneKey, loggingReason: String) -> Unit, ) { + check(scene.key != SceneKey.Gone || deviceUnlockedInteractor.isDeviceUnlocked.value) { + "Cannot change to the Gone scene while the device is locked. Logging reason for scene" + + " change was: $loggingReason" + } + val currentSceneKey = desiredScene.value.key if (currentSceneKey == scene.key) { return diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index a894f877fe3c..11da2376328a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -188,7 +188,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mTestScope.getBackgroundScope(), mKosmos.getFakeSceneContainerConfig()), powerInteractor, - mock(SceneLogger.class)); + mock(SceneLogger.class), + mKosmos.getDeviceUnlockedInteractor()); FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index cbd4d2bfe377..8f46a37bf540 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -220,7 +220,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { mTestScope.getBackgroundScope(), mKosmos.getFakeSceneContainerConfig()), powerInteractor, - mock(SceneLogger.class)); + mock(SceneLogger.class), + mKosmos.getDeviceUnlockedInteractor()); FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); KeyguardInteractor keyguardInteractor = new KeyguardInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 1ed045ff6546..589f7c23ea13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -419,7 +419,8 @@ public class BubblesTest extends SysuiTestCase { mTestScope.getBackgroundScope(), mKosmos.getFakeSceneContainerConfig()), powerInteractor, - mock(SceneLogger.class)); + mock(SceneLogger.class), + mKosmos.getDeviceUnlockedInteractor()); FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags(); KeyguardInteractor keyguardInteractor = new KeyguardInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt index b600b50b8d2d..8dcdd3a9425c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt @@ -38,5 +38,6 @@ val Kosmos.deviceEntryInteractor by deviceEntryFaceAuthRepository = deviceEntryFaceAuthRepository, trustRepository = trustRepository, flags = sceneContainerFlags, + deviceUnlockedInteractor = deviceUnlockedInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt new file mode 100644 index 000000000000..df1cdc2f72cb --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * 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.deviceentry.domain.interactor + +import com.android.systemui.authentication.domain.interactor.authenticationInteractor +import com.android.systemui.deviceentry.data.repository.deviceEntryRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.applicationCoroutineScope + +val Kosmos.deviceUnlockedInteractor by Fixture { + DeviceUnlockedInteractor( + applicationScope = applicationCoroutineScope, + authenticationInteractor = authenticationInteractor, + deviceEntryRepository = deviceEntryRepository, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 24670b12193a..cc0449d7e7bb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.kosmos import android.content.applicationContext @@ -24,6 +26,8 @@ import com.android.systemui.classifier.falsingCollector import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.communal.data.repository.fakeCommunalRepository +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -36,6 +40,7 @@ import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.statusbar.phone.screenOffAnimationController import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository import com.android.systemui.util.time.systemClock +import kotlinx.coroutines.ExperimentalCoroutinesApi /** Helper for using [Kosmos] from Java. */ @Deprecated("Please convert your test to Kotlin and use [Kosmos] directly.") @@ -65,6 +70,8 @@ class KosmosJavaAdapter( val sceneInteractor by lazy { kosmos.sceneInteractor } val falsingCollector by lazy { kosmos.falsingCollector } val powerInteractor by lazy { kosmos.powerInteractor } + val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor } + val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } init { kosmos.applicationContext = testCase.context diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index 998987602234..fc023758fdf6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.power.domain.interactor.powerInteractor @@ -29,5 +30,6 @@ val Kosmos.sceneInteractor by repository = sceneContainerRepository, powerInteractor = powerInteractor, logger = sceneLogger, + deviceUnlockedInteractor = deviceUnlockedInteractor, ) } |