Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ad1fe4a authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge changes Ib0e54884,If52184b3 into main

* changes:
  [flexiglass] Fixes formatting issues in scene.md
  [flexiglass] Prevent unintentional switching to the Gone scene.
parents 77f6f357 1db1508f
Loading
Loading
Loading
Loading
−18.7 KiB (12.6 KiB)
Loading image diff...
+60 −50
Original line number Diff line number Diff line
@@ -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: `console $
adb shell device_config put systemui com.android.systemui.scene_container false`
To **disable** the framework, simply turn off the main aconfig flag:

```console
$ adb shell device_config put systemui com.android.systemui.scene_container false
```

## Defining a scene

@@ -118,11 +127,12 @@ 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:

```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(
@@ -136,10 +146,9 @@ override fun SceneScope.Content(
    ) {
        // 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).
+3 −0
Original line number Diff line number Diff line
@@ -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(
+2 −0
Original line number Diff line number Diff line
@@ -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()

+115 −0
Original line number Diff line number Diff line
/*
 * 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()
        }
}
Loading