summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/docs/imgs/ribbon.pngbin32027 -> 12880 bytes
-rw-r--r--packages/SystemUI/docs/scene.md110
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt115
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt2
18 files changed, 337 insertions, 66 deletions
diff --git a/packages/SystemUI/docs/imgs/ribbon.png b/packages/SystemUI/docs/imgs/ribbon.png
index 9f5765232aed..3379d3d95025 100644
--- a/packages/SystemUI/docs/imgs/ribbon.png
+++ b/packages/SystemUI/docs/imgs/ribbon.png
Binary files differ
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,
)
}