diff options
| author | 2023-12-30 14:07:21 -0800 | |
|---|---|---|
| committer | 2023-12-30 14:21:26 -0800 | |
| commit | dea748aa562b6b7d38a91c2e4fe7b9fd5bd8f904 (patch) | |
| tree | 146120eddae1a019bef35e8efcb8ada625258892 | |
| parent | c61f0302a6230c8ca688e57db17b8190b1567cf7 (diff) | |
[flexiglass] Adds AOSP documentation.
Bug: 283121968
Change-Id: I6e81cadf09b7727ea5ec91c8bfa09a0f9d2c6909
Flag: N/A
Test: N/A
| -rw-r--r-- | packages/SystemUI/docs/imgs/ribbon.png | bin | 0 -> 32027 bytes | |||
| -rw-r--r-- | packages/SystemUI/docs/scene.md | 297 |
2 files changed, 297 insertions, 0 deletions
diff --git a/packages/SystemUI/docs/imgs/ribbon.png b/packages/SystemUI/docs/imgs/ribbon.png Binary files differnew file mode 100644 index 000000000000..9f5765232aed --- /dev/null +++ b/packages/SystemUI/docs/imgs/ribbon.png diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md new file mode 100644 index 000000000000..3e4a1b4a05c7 --- /dev/null +++ b/packages/SystemUI/docs/scene.md @@ -0,0 +1,297 @@ +# The Scene Framework + +Known internally as "Flexiglass", this framework defines a graph where each node +is a "scene" and each edge between the scenes is a transition. The scenes are +the main components of System UI, on phones these are: the lockscreen, bouncer, +shade, and quick settings panels/views/screens). Each scene is a standalone +experience. + +The **main goal** of the framework is to increase code health by applying +[Separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) +over several dimensions: + +1. Each scene is a standalone piece of UI; their code doesn't need to concern + itself with either transition animations or anything in other scenes. This + frees the developer to be able to focus only on the content of the UI for + that scene. +2. Transition definitions (which scene leads to which other scene following + which user action) are pulled out and separated from the content of the UI. +3. Transition animations (the effects that happen alongside the gradual change + 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. + +## Terminology + +* **Scene** a collection of UI elements in a layout that, together, make up a + "screen" or "page" that is as large as the container. Scenes can be + navigated between / transition to/from. To learn more, please see + [this section](#Defining-a-scene). +* **Element** (or "UI element") a single unit of UI within a scene. One scene + can arrange multiple elements within a layout structure. +* **Transition** the gradual switching from one scene to another scene. There + are two kinds: [user-driven](Scene-navigation) and + [automatic](Automatic-scene-transitions) scene transitions. +* **Transition animation** the set of UI effects that occurs while/during a + transition. These can apply to the entire scene or to specific elements in + the scene. To learn more, please see + [this section](#Scene-transition-animations). +* **Scene container** (or just "container") the root piece of UI (typically a + `@Composable` function) that sets up all the scenes, their transitions, etc. + To learn more, please see [this section](#Scene-container). +* **Container configuration** (or just "configuration") the collection of + scenes and some added information about the desired behaviour of a + container. To learn more, please see + [this section](#Scene-container-configuration). + +## Enabling the framework + +As of the end of 2023, the scene framework is under development; as such, it is +disabled by default. For those who are interested in a preview, please follow +the instructions below to turn it on. + +NOTE: in case these instructions become stale and don't actually enable the +framework, please make sure `SceneContainerFlag.isEnabled` in the +[`SceneContainerFlags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt) +file evalutes to `true`. + +1. Set **`SCENE_CONTAINER_ENABLED`** to `true` in the + [`Flags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/flags/Flags.kt) + file +2. Set the **`migrate_keyguard_status_bar_view`** classic flag to `true` by + 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` +5. **Verify** that the scene framework was turned on. There are two ways to do + this: + + *(a)* look for the sash/ribbon UI at the bottom-right corner of the display: +  + + NOTE: this will be removed proper to the actual release of the framework. + + *(b)* Turn on logging and look for the logging statements in `logcat`: + ```console + + # Turn on logging from the framework: + + $ adb shell cmd statusbar echo -b SceneFramework:verbose + +# Look for the log statements from the framework: + +$ 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` + +## Defining a scene + +Each scene is defined as an implementation of the +[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt) +interface, which has three parts: 1. The `key` property returns the +[`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt) +that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns +the (potentially ever-changing) set of navigation edges to other scenes, based +on user-actions, which is how the navigation graph is defined (see +[the Scene navigation](#Scene-navigation) section for more) 3. The `Content` +function which uses +[Jetpack Compose](https://developer.android.com/jetpack/compose) to declare of +the UI itself. This is the UI "at rest", e.g. once there is no transition +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 + +``` +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 +[`SceneModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt;l=35-50;drc=564f233d5b597aedf06961c76e582464eebe8ba6). + +## Scene navigation + +As seen above, each scene is responsible for providing an observable `Flow` of a +`Map` that connects `UserAction` (for example: swipe down, swipe up, back +button/gesture, etc.) keys to `SceneModel` destinations. This is how the scene +navigation graph is defined. + +NOTE: this controls *only* user-input based navigation. To learn about the other +type of scene navigation, please see the +[Automatic scene transitions](#Automatic-scene-transitions) section. + +Because this is a `Flow`, scene implemetations should feel free to emit new +values over time. For example, the `Lockscreen` scene ties the "swipe up" user +action to go to the `Bouncer` scene if the device is still locked or to go to +the `Gone` scene if the device is unlocked, allowing the user to dismiss the +lockscreen UI when not locked. + +## Scene transition animations + +The Scene Framework separates transition animations from content UI declaration +by placing the definition of the former in a different location. This way, +there's no longer a need to contaminate the content UI declaration with +animation logic, a practice that becomes unscalable over time. + +Under the hood, the Scene Framework uses +[`SceneTransitionLayout`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt), +a `@Composable` function designed with scene graph and transitions in mind. In +fact, the Scene Framework is merely a shallow wrapper around +`SceneTransitionLayout`. + +The `SceneTransitionLayout` API requires the transitions to be passed-in +separately from the scenes themselves. In System UI, the transitions can be +found in +[`SceneContainerTransitions`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt). +As you can see, each possible scene-to-scene transition has its own builder, +here's one example: + +```kotlin +fun TransitionBuilder.lockscreenToShadeTransition() { + spec = tween(durationMillis = 500) + + punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim) + translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false) + fractionRange(end = 0.5f) { + fade(Shade.Elements.ScrimBackground) + translate( + QuickSettings.Elements.CollapsedGrid, + Edge.Top, + startsOutsideLayoutBounds = false, + ) + } + fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) } +} +``` + +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 + +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). + +### Tagging elements + +As demonstrated above, elements within a scene can be addressed from transition +defintions. In order to "tag" an element with a specific `ElementKey`, the +[`element` modifier](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt) +must be used on the composable that declared that element's UI: + +```kotlin +Text( + text = "Some text", + modifier = Modifier.element(MyElements.SomeText), +) +``` + +In addition to the ability to refer to a tagged element in transition +definitions, if the same `ElementKey` is used for one element in the current +scene and another element in the destination scene, the element is considered to +be a **shared element**. As such, the framework automatically translates and +scales the bounds of the shared element from its current bounds in the source +scene to its final bounds in the destination scene. + +## Scene container + +To set up a scene framework instance, a scene container must be declared. This +is the root of an entire scene graph that puts together the scenes, their +transitions, and the configuration. The container is then added to a parent +`@Composable` or `View` so it can be displayed. + +The default scene container in System UI is defined in the +[`SceneContainer.kt` file](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt). + +### Scene container configuration + +The `SceneContainer` function is passed a few parameters including a view-model +and a set of scenes. The exact details of what gets passed in depends on the +[`SceneContainerConfig` object](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt) +which is injected into the Dagger dependency graph +[here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt). + +## Automatic scene transitions + +The scene framework supports the ability for scenes to change automatically +based on device state or events other than direct user input. For example: when +the device is locked, there's an automatic scene transition to the `Lockscreen` +scene. + +This logic is contained within the +[`SceneContainerStartable`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt) +class. + +## Side-effects + +Similarly to [the above](#Automatic-scene-transitions), the +`SceneContainerStartable` also handles side-effects by updating other parts of +the System UI codebase whenever internal scene framework state changes. As an +example: the visibility of the `View` that contains our +[scene container](#Scene-container) is updated every time there's a transition +to or from the `Gone` scene. + +## Observing scene transition state + +There are a couple of ways to observe the transition state: + +1. [Easiest] using the `SceneScope` of the scene container, simply use the + `animateSharedXAsState` API, the full list is + [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt). +2. [Harder] if outside the `SceneScope` of the scene container, observe + [`SceneInteractor.transitionState`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt;l=88;drc=af57d5e49431c6728e7cf192bada88e0541ebf0c). + +## Dependency Injection + +The entire framework is provided into the Dagger dependency graph from the +top-level Dagger module at +[`SceneContainerFrameworkModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt) +this puts together the scenes from `SceneModule`, the configuration from +`SceneContainerConfigModule`, and the startable from +`SceneContainerStartableModule`. |