Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 1 | # The Scene Framework |
| 2 | |
| 3 | Known internally as "Flexiglass", this framework defines a graph where each node |
| 4 | is a "scene" and each edge between the scenes is a transition. The scenes are |
| 5 | the main components of System UI, on phones these are: the lockscreen, bouncer, |
| 6 | shade, and quick settings panels/views/screens). Each scene is a standalone |
| 7 | experience. |
| 8 | |
| 9 | The **main goal** of the framework is to increase code health by applying |
| 10 | [Separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) |
| 11 | over several dimensions: |
| 12 | |
| 13 | 1. Each scene is a standalone piece of UI; their code doesn't need to concern |
| 14 | itself with either transition animations or anything in other scenes. This |
| 15 | frees the developer to be able to focus only on the content of the UI for |
| 16 | that scene. |
| 17 | 2. Transition definitions (which scene leads to which other scene following |
| 18 | which user action) are pulled out and separated from the content of the UI. |
| 19 | 3. Transition animations (the effects that happen alongside the gradual change |
| 20 | from one scene to another) are also pulled out and separated from the |
| 21 | content of the UI. |
| 22 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 23 | In addition to the above, some of the **secondary goals** are: |
| 24 | |
| 25 | 4. Make **customization easier**: by separating scenes to standalone pieces, it |
| 26 | becomes possible for variant owners and OEMs to exclude or replace certain scenes |
| 27 | or to add brand-new scenes. |
| 28 | 5. **Enable modularization**: by separating scenes to standalone pieces, it |
| 29 | becomes possible to break down System UI into smaller codebases, each one of |
| 30 | which could be built on its own. Note: this isn't part of the scene framework |
| 31 | itself but is something that can be done more easily once the scene framework |
| 32 | is in place. |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 33 | |
| 34 | ## Terminology |
| 35 | |
| 36 | * **Scene** a collection of UI elements in a layout that, together, make up a |
| 37 | "screen" or "page" that is as large as the container. Scenes can be |
| 38 | navigated between / transition to/from. To learn more, please see |
| 39 | [this section](#Defining-a-scene). |
| 40 | * **Element** (or "UI element") a single unit of UI within a scene. One scene |
| 41 | can arrange multiple elements within a layout structure. |
| 42 | * **Transition** the gradual switching from one scene to another scene. There |
| 43 | are two kinds: [user-driven](Scene-navigation) and |
| 44 | [automatic](Automatic-scene-transitions) scene transitions. |
| 45 | * **Transition animation** the set of UI effects that occurs while/during a |
| 46 | transition. These can apply to the entire scene or to specific elements in |
| 47 | the scene. To learn more, please see |
| 48 | [this section](#Scene-transition-animations). |
| 49 | * **Scene container** (or just "container") the root piece of UI (typically a |
| 50 | `@Composable` function) that sets up all the scenes, their transitions, etc. |
| 51 | To learn more, please see [this section](#Scene-container). |
| 52 | * **Container configuration** (or just "configuration") the collection of |
| 53 | scenes and some added information about the desired behaviour of a |
| 54 | container. To learn more, please see |
| 55 | [this section](#Scene-container-configuration). |
| 56 | |
| 57 | ## Enabling the framework |
| 58 | |
| 59 | As of the end of 2023, the scene framework is under development; as such, it is |
| 60 | disabled by default. For those who are interested in a preview, please follow |
| 61 | the instructions below to turn it on. |
| 62 | |
| 63 | NOTE: in case these instructions become stale and don't actually enable the |
| 64 | framework, please make sure `SceneContainerFlag.isEnabled` in the |
Alejandro Nijamkin | 63f3c7b | 2024-04-30 14:44:45 -0700 | [diff] [blame] | 65 | [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt) |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 66 | file evalutes to `true`. |
| 67 | |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 68 | 1. Set a collection of **aconfig flags** to `true` by running the following |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 69 | commands: |
| 70 | ```console |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 71 | $ adb shell device_config override systemui com.android.systemui.keyguard_bottom_area_refactor true |
| 72 | $ adb shell device_config override systemui com.android.systemui.keyguard_wm_state_refactor true |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 73 | $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true |
Ale Nijamkin | f4296f1 | 2024-09-17 17:29:08 +0000 | [diff] [blame] | 74 | $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 75 | $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true |
| 76 | $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true |
Ale Nijamkin | f4296f1 | 2024-09-17 17:29:08 +0000 | [diff] [blame] | 77 | $ adb shell device_config override systemui com.android.systemui.scene_container true |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 78 | ``` |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 79 | 2. **Restart** System UI by issuing the following command: |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 80 | ```console |
| 81 | $ adb shell am crash com.android.systemui |
| 82 | ``` |
Caitlin Shkuratov | f12f713 | 2024-04-24 19:08:41 +0000 | [diff] [blame] | 83 | 3. **Verify** that the scene framework was turned on. There are two ways to do |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 84 | this: |
| 85 | |
| 86 | *(a)* look for the sash/ribbon UI at the bottom-right corner of the display: |
| 87 |  |
| 88 | |
| 89 | NOTE: this will be removed proper to the actual release of the framework. |
| 90 | |
| 91 | *(b)* Turn on logging and look for the logging statements in `logcat`: |
| 92 | ```console |
| 93 | |
| 94 | # Turn on logging from the framework: |
| 95 | |
| 96 | $ adb shell cmd statusbar echo -b SceneFramework:verbose |
| 97 | |
Alejandro Nijamkin | 83f1972 | 2024-01-18 15:17:57 -0800 | [diff] [blame] | 98 | ### Checking if the framework is enabled |
| 99 | |
| 100 | Look for the log statements from the framework: |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 101 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 102 | ```console |
| 103 | $ adb logcat -v time SceneFramework:* *:S |
| 104 | ``` |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 105 | |
Alejandro Nijamkin | 83f1972 | 2024-01-18 15:17:57 -0800 | [diff] [blame] | 106 | ### Disabling the framework |
| 107 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 108 | To **disable** the framework, simply turn off the main aconfig flag: |
| 109 | |
| 110 | ```console |
| 111 | $ adb shell device_config put systemui com.android.systemui.scene_container false |
| 112 | ``` |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 113 | |
| 114 | ## Defining a scene |
| 115 | |
Alejandro Nijamkin | 83f1972 | 2024-01-18 15:17:57 -0800 | [diff] [blame] | 116 | By default, the framework ships with fully functional scenes as enumarated |
| 117 | [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt). |
| 118 | Should a variant owner or OEM want to replace or add a new scene, they could |
| 119 | do so by defining their own scene. This section describes how to do that. |
| 120 | |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 121 | Each scene is defined as an implementation of the |
burakov | a398c98 | 2024-09-02 12:22:58 +0000 | [diff] [blame] | 122 | [`Scene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt) |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 123 | interface, which has three parts: 1. The `key` property returns the |
| 124 | [`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt) |
burakov | 2f38380 | 2024-09-02 13:31:17 +0000 | [diff] [blame] | 125 | that uniquely identifies that scene 2. The `userActions` `Flow` returns |
| 126 | the (potentially ever-changing) set of navigation edges to other content, based |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 127 | on user-actions, which is how the navigation graph is defined (see |
| 128 | [the Scene navigation](#Scene-navigation) section for more) 3. The `Content` |
| 129 | function which uses |
| 130 | [Jetpack Compose](https://developer.android.com/jetpack/compose) to declare of |
| 131 | the UI itself. This is the UI "at rest", e.g. once there is no transition |
| 132 | between any two scenes. The Scene Framework has other ways to define how the |
| 133 | content of your UI changes with and throughout a transition to learn more please |
| 134 | see the [Scene transition animations](#Scene-transition-animations) section |
| 135 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 136 | For example: |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 137 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 138 | ```kotlin |
burakov | a398c98 | 2024-09-02 12:22:58 +0000 | [diff] [blame] | 139 | @SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : Scene { |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 140 | override val key = SceneKey.YourScene |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 141 | |
burakov | 2f38380 | 2024-09-02 13:31:17 +0000 | [diff] [blame] | 142 | override val userActions: StateFlow<Map<UserAction, SceneModel>> = |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 143 | MutableStateFlow<Map<UserAction, SceneModel>>( |
| 144 | mapOf( |
| 145 | // This is where scene navigation is defined, more on that below. |
| 146 | ) |
| 147 | ).asStateFlow() |
| 148 | |
| 149 | @Composable |
| 150 | override fun SceneScope.Content( |
| 151 | modifier: Modifier, |
| 152 | ) { |
| 153 | // This is where the UI is defined using Jetpack Compose. |
| 154 | } |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 155 | } |
| 156 | ``` |
| 157 | |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 158 | ### Injecting scenes |
| 159 | |
| 160 | Scenes are injected into the Dagger dependency graph from the |
| 161 | [`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). |
| 162 | |
| 163 | ## Scene navigation |
| 164 | |
| 165 | As seen above, each scene is responsible for providing an observable `Flow` of a |
| 166 | `Map` that connects `UserAction` (for example: swipe down, swipe up, back |
| 167 | button/gesture, etc.) keys to `SceneModel` destinations. This is how the scene |
| 168 | navigation graph is defined. |
| 169 | |
| 170 | NOTE: this controls *only* user-input based navigation. To learn about the other |
| 171 | type of scene navigation, please see the |
| 172 | [Automatic scene transitions](#Automatic-scene-transitions) section. |
| 173 | |
| 174 | Because this is a `Flow`, scene implemetations should feel free to emit new |
| 175 | values over time. For example, the `Lockscreen` scene ties the "swipe up" user |
| 176 | action to go to the `Bouncer` scene if the device is still locked or to go to |
| 177 | the `Gone` scene if the device is unlocked, allowing the user to dismiss the |
| 178 | lockscreen UI when not locked. |
| 179 | |
| 180 | ## Scene transition animations |
| 181 | |
| 182 | The Scene Framework separates transition animations from content UI declaration |
| 183 | by placing the definition of the former in a different location. This way, |
| 184 | there's no longer a need to contaminate the content UI declaration with |
| 185 | animation logic, a practice that becomes unscalable over time. |
| 186 | |
| 187 | Under the hood, the Scene Framework uses |
| 188 | [`SceneTransitionLayout`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt), |
| 189 | a `@Composable` function designed with scene graph and transitions in mind. In |
| 190 | fact, the Scene Framework is merely a shallow wrapper around |
| 191 | `SceneTransitionLayout`. |
| 192 | |
| 193 | The `SceneTransitionLayout` API requires the transitions to be passed-in |
| 194 | separately from the scenes themselves. In System UI, the transitions can be |
| 195 | found in |
| 196 | [`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). |
| 197 | As you can see, each possible scene-to-scene transition has its own builder, |
| 198 | here's one example: |
| 199 | |
| 200 | ```kotlin |
| 201 | fun TransitionBuilder.lockscreenToShadeTransition() { |
| 202 | spec = tween(durationMillis = 500) |
| 203 | |
| 204 | punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim) |
| 205 | translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false) |
| 206 | fractionRange(end = 0.5f) { |
| 207 | fade(Shade.Elements.ScrimBackground) |
| 208 | translate( |
| 209 | QuickSettings.Elements.CollapsedGrid, |
| 210 | Edge.Top, |
| 211 | startsOutsideLayoutBounds = false, |
| 212 | ) |
| 213 | } |
| 214 | fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) } |
| 215 | } |
| 216 | ``` |
| 217 | |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 218 | Going through the example code: |
| 219 | |
| 220 | * The `spec` is the animation that should be invoked, in the example above, we use a `tween` |
| 221 | animation with a duration of 500 milliseconds |
| 222 | * Then there's a series of function calls: `punchHole` applies a clip mask to the `Scrim` |
| 223 | element in the destination scene (in this case it's the `Shade` scene) which has the |
| 224 | position and size determined by the `bounds` parameter and the shape passed into the `shape` |
| 225 | parameter. This lets the `Lockscreen` scene render "through" the `Shade` scene |
| 226 | * The `translate` call shifts the `Scrim` element to/from the `Top` edge of the scene container |
| 227 | * The first `fractionRange` wrapper tells the system to apply its contained functions |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 228 | only during the first half of the transition. Inside of it, we see a `fade` of |
| 229 | the `ScrimBackground` element and a `translate` o the `CollpasedGrid` element |
Ale Nijamkin | 1db1508 | 2024-01-18 22:15:02 +0000 | [diff] [blame] | 230 | to/from the `Top` edge |
| 231 | * The second `fractionRange` only starts at the second half of the transition (e.g. when |
| 232 | the previous one ends) and applies a `fade` on the `Notifications` element |
Alejandro Nijamkin | dea748a | 2023-12-30 14:07:21 -0800 | [diff] [blame] | 233 | |
| 234 | You can find the actual documentation for this API |
| 235 | [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt). |
| 236 | |
| 237 | ### Tagging elements |
| 238 | |
| 239 | As demonstrated above, elements within a scene can be addressed from transition |
| 240 | defintions. In order to "tag" an element with a specific `ElementKey`, the |
| 241 | [`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) |
| 242 | must be used on the composable that declared that element's UI: |
| 243 | |
| 244 | ```kotlin |
| 245 | Text( |
| 246 | text = "Some text", |
| 247 | modifier = Modifier.element(MyElements.SomeText), |
| 248 | ) |
| 249 | ``` |
| 250 | |
| 251 | In addition to the ability to refer to a tagged element in transition |
| 252 | definitions, if the same `ElementKey` is used for one element in the current |
| 253 | scene and another element in the destination scene, the element is considered to |
| 254 | be a **shared element**. As such, the framework automatically translates and |
| 255 | scales the bounds of the shared element from its current bounds in the source |
| 256 | scene to its final bounds in the destination scene. |
| 257 | |
| 258 | ## Scene container |
| 259 | |
| 260 | To set up a scene framework instance, a scene container must be declared. This |
| 261 | is the root of an entire scene graph that puts together the scenes, their |
| 262 | transitions, and the configuration. The container is then added to a parent |
| 263 | `@Composable` or `View` so it can be displayed. |
| 264 | |
| 265 | The default scene container in System UI is defined in the |
| 266 | [`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). |
| 267 | |
| 268 | ### Scene container configuration |
| 269 | |
| 270 | The `SceneContainer` function is passed a few parameters including a view-model |
| 271 | and a set of scenes. The exact details of what gets passed in depends on the |
| 272 | [`SceneContainerConfig` object](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt) |
| 273 | which is injected into the Dagger dependency graph |
| 274 | [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt). |
| 275 | |
| 276 | ## Automatic scene transitions |
| 277 | |
| 278 | The scene framework supports the ability for scenes to change automatically |
| 279 | based on device state or events other than direct user input. For example: when |
| 280 | the device is locked, there's an automatic scene transition to the `Lockscreen` |
| 281 | scene. |
| 282 | |
| 283 | This logic is contained within the |
| 284 | [`SceneContainerStartable`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt) |
| 285 | class. |
| 286 | |
| 287 | ## Side-effects |
| 288 | |
| 289 | Similarly to [the above](#Automatic-scene-transitions), the |
| 290 | `SceneContainerStartable` also handles side-effects by updating other parts of |
| 291 | the System UI codebase whenever internal scene framework state changes. As an |
| 292 | example: the visibility of the `View` that contains our |
| 293 | [scene container](#Scene-container) is updated every time there's a transition |
| 294 | to or from the `Gone` scene. |
| 295 | |
| 296 | ## Observing scene transition state |
| 297 | |
| 298 | There are a couple of ways to observe the transition state: |
| 299 | |
| 300 | 1. [Easiest] using the `SceneScope` of the scene container, simply use the |
| 301 | `animateSharedXAsState` API, the full list is |
| 302 | [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt). |
| 303 | 2. [Harder] if outside the `SceneScope` of the scene container, observe |
| 304 | [`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). |
| 305 | |
| 306 | ## Dependency Injection |
| 307 | |
| 308 | The entire framework is provided into the Dagger dependency graph from the |
| 309 | top-level Dagger module at |
| 310 | [`SceneContainerFrameworkModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt) |
| 311 | this puts together the scenes from `SceneModule`, the configuration from |
| 312 | `SceneContainerConfigModule`, and the startable from |
| 313 | `SceneContainerStartableModule`. |
Alejandro Nijamkin | 83f1972 | 2024-01-18 15:17:57 -0800 | [diff] [blame] | 314 | |
| 315 | ## Integration Notes |
| 316 | |
| 317 | ### Relationship to Jetpack Compose |
| 318 | |
| 319 | The scene framework depends on Jetpack Compose; therefore, compiling System UI with |
| 320 | Jetpack Compose is required. However, because Jetpack Compose and Android Views |
| 321 | [interoperate](https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose), |
| 322 | the UI in each scene doesn't necessarily need to be a pure hierarchy of `@Composable` |
| 323 | functions; instead, it's acceptable to use an `AndroidView` somewhere in the |
| 324 | hierarchy of composable functions to include a `View` or `ViewGroup` subtree. |
| 325 | |
| 326 | #### Interoperability with Views |
| 327 | The scene framework comes with built-in functionality to animate the entire scene and/or |
| 328 | elements within the scene in-tandem with the actual scene transition progress. |
| 329 | |
| 330 | For example, as the user drags their finger down rom the top of the lockscreen, |
| 331 | the shade scene becomes visible and gradually expands, the amount of expansion tracks |
| 332 | the movement of the finger. |
| 333 | |
| 334 | That feature of the framework uses a custom `element(ElementKey)` Jetpack Compose |
| 335 | `Modifier` to refer to elements within a scene. |
| 336 | The transition builders then use the same `ElementKey` objects to refer to those elements |
| 337 | and describe how they animate in-tandem with scene transitions. Because this is a |
| 338 | Jetpack Compose `Modifier`, it means that, in order for an element in a scene to be |
| 339 | animated automatically by the framework, that element must be nested within a pure |
| 340 | `@Composable` hierarchy. The element itself is allowed to be a classic Android `View` |
| 341 | (nested within a Jetpack Compose `AndroidView`) but all ancestors must be `@Composable` |
| 342 | functions. |
| 343 | |
| 344 | ### Notifications |
| 345 | |
| 346 | As of January 2024, the integration of notifications and heads-up notifications (HUNs) |
| 347 | into the scene framework follows an unusual pattern. We chose this pattern due to migration |
| 348 | risk and performance concerns but will eventually replace it with the more common element |
| 349 | placement pattern that all other elements are following. |
| 350 | |
| 351 | The special pattern for notifications is that, instead of the notification list |
| 352 | (`NotificationStackScrollLayout` or "NSSL", which also displays HUNs) being placed in the element |
| 353 | hierarchy within the scenes that display notifications, the NSSL (which continues to be an Android View) |
| 354 | "floats" above the scene container, rendering on top of everything. This is very similar to |
| 355 | how NSSL is integrated with the legacy shade, prior to the scene framework. |
| 356 | |
| 357 | In order to render the NSSL as if it's part of the organic hierarchy of elements within its |
| 358 | scenes, we control the NSSL's self-imposed effective bounds (e.g. position offsets, clip path, |
| 359 | size) from `@Composable` elements within the normal scene hierarchy. These special |
| 360 | "placeholder" elements can be found |
| 361 | [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt). |
| 362 | |