From c64aab6b77d508b1f4f655d5f4045a7f6190fc33 Mon Sep 17 00:00:00 2001 From: Jeremy LaCivita Date: Fri, 8 Dec 2023 13:42:03 -0500 Subject: [PATCH] fix: latest udpates --- .../specifications/lifecycle/index.md | 70 +++++---- .../specifications/lifecycle/presentation.md | 134 +++++++++--------- 2 files changed, 98 insertions(+), 106 deletions(-) diff --git a/requirements/specifications/lifecycle/index.md b/requirements/specifications/lifecycle/index.md index 1b64badd1..72922bc9d 100644 --- a/requirements/specifications/lifecycle/index.md +++ b/requirements/specifications/lifecycle/index.md @@ -1,11 +1,11 @@ # App Lifecycle Management -Document Status: Working Draft +Document Status: Candidate Specification See [Firebolt Requirements Governance](../../governance.md) for more info. | Contributor | Organization | -| ------------------------- | ------------ | +|---------------------------|--------------| | Andrew Bennett | Sky | | Cody Bonney | Charter | | Bart Catrysse | Liberty | @@ -36,7 +36,7 @@ first, if needed. For details on presentation, see [App Presentation](./presentation.md). *Closing* an app refers to the process of getting an app out of a state where -it is the primary user experience (e.g not visible, not audible, and not +it is the primary user experience (e.g. not visible, not audible, and not responding to input). This **does not** involve *destroying* the app. *Suspending* an app refers to reducing the app's memory and CPU usage, and @@ -81,7 +81,7 @@ a compatible process so that it resumes where it left off. - [6. Create Parameters](#6-create-parameters) - [7. Core SDK APIs](#7-core-sdk-apis) - [7.1. Application Interface](#71-application-interface) - - [7.2. Activatible Interface](#72-activatible-interface) + - [7.2. Activatable Interface](#72-activatable-interface) - [7.3. Sleepable Interface](#73-sleepable-interface) - [7.4. Example App](#74-example-app) - [7.5. Ready](#75-ready) @@ -92,8 +92,8 @@ a compatible process so that it resumes where it left off. ## 3. Lifecycle States -Firebolt platforms **MUST** support running one or more apps concurrently. The -platform **MUST** manage transition of apps from state to state. +Firebolt platforms **MUST** support running one or more apps concurrently. The +platform **MUST** manage transition of apps from state to state. A Firebolt app, once running, **MUST** be in one of several states and **MUST NOT** be in more than one state at a time. @@ -127,7 +127,6 @@ on this. ### 3.1. Initializing - This is the initial state an app exists from the moment it starts receiving CPU cycles. @@ -198,8 +197,8 @@ Apps in this state **SHOULD** reduce memory usage, if possible. When an app transitions to this state, the platform **MUST** dispatch the `Lifecycle.onStateChanged` notification with the current state. -### 3.4. Suspended +### 3.4. Suspended This state allows an app to remain in memory and consume fewer resources. @@ -220,8 +219,8 @@ Apps in this state **SHOULD** further reduce memory usage (more so than in the **TODO**:: add all the transition pre-requisites, e.g. Apps **MUST** only enter this state from the `SUSPENDED` state, via the `sleep()` interface. -### 3.5. Sleeping +### 3.5. Sleeping This state allows an app to be copied from memory to local storage and then terminated to save resources. Subsequently, the app may be copied back into @@ -246,6 +245,7 @@ If a platform supports copying sleeping apps memory out of RAM then: > > Finally, the app and its container **MAY** be removed from memory and have > other resources released as well. + ## 4. Getting the current state The Lifecycle module **MUST** provide a `state` property API that returns the @@ -268,6 +268,7 @@ The `state` API must have one of the following values: Note that the `onStateChanged` notification **MUST** never be dispatched for the `Sleeping` state since it would not be received anyway. + ## 5. Lifecycle State Transitions There are several state transitions where the app and the platform need to @@ -278,6 +279,7 @@ States](../../images/specifications/lifecycle/lifecycle-transitions.png) As an app changes states the platform will invoke specific app-provided transition methods from the `Application` interface: + | | Legend | | - | ------------- | | ↓ | Limited | @@ -290,24 +292,22 @@ transition methods from the `Application` interface: | `resume()` | ✔ | ✔ | ✔ | ✔ | | graphics surface reallocated, full memory usage and normal CPU cycles. | | `destroy()` | ✔/↓ | ✔/↓ | ✔ | | | Preprare for the app to be deallocated and removed from execution. CPU & RAM based on previous state. | - If an app implements the `Activity` interface, then the following transitions may be invoked: + | | CPU | RAM | Net | GFX | A/V | Description | |----------------|-----|-----|-----|-----|-----|---------------------------------------------------------------------------| | `activate()` | ✔ | ✔ | ✔ | ✔ | ✔ | App is expected to become a user-perceptible part of the user experience. | | `deactivate()` | ✔ | ✔ | ✔ | ✔ | ✔ | Must remove any user-perceptible activities and deallocate A/V decoders. | - - Finally, if an app implements the `Sleepable` interface, then the following transistions may be invoked. + | | CPU | RAM | Net | GFX | A/V | Description | |-----------|-----|-----|-----|-----|-----|----------------------------------------------------------------------------------------| | `sleep()` | ↓ | ↓ | ✔ | | | Prepare for an extended period with no CPU cycles given to app. | | `wake()` | ↓ | ↓ | ✔ | | | Cleanup after an extended period with no CPU, e.g. reset timers / network connections. | - All of these transition APIs are blocking, and each one has a platform-configurable timeout that specifies how long the app has to fulfill the method. @@ -319,6 +319,7 @@ All Firebolt apps **MUST** implement the `Application` interface, `xrn:firebolt:capability:lifecycle:application`. This includes: + - `Application.create()` - `Application.suspend()` - `Application.resume()` @@ -331,17 +332,19 @@ how resources are managed during these state transitions. See [Application Interface](#71-application-interface) for more info. User-facing apps **MUST** implement the `Activity` interface, -`xrn:firebolt:capability:lifecycle:activatible`. +`xrn:firebolt:capability:lifecycle:activatable`. This includes: + - `Application.activate()` - `Application.deactivate()` By providing an implementation of the -`xrn:firebolt:capability:lifecycle:activatible` interface, an app can influence +`xrn:firebolt:capability:lifecycle:activatable` interface, an app can influence how resources are managed during these state transitions. See [Activity Interface](#82-activity-interface) for more info. + ### 5.1. Initializing an app Once an app is loaded it **MUST** be initialized immediately. @@ -386,20 +389,18 @@ for [Parameters](#6-create-parameters) and prepare to fulfill the provided launch configuration. Example Launch Parameters: + ```json { "preload": true, "preloadReason": "boot" } - ``` -**TODO**: discuss this ^^ - Once the `create` method returns the app **MUST** be immediately transitioned to the `RUNNING` state. -### 5.2. Activating an app +### 5.2. Activating an app Activating an app transitions it to the `ACTIVE` state so that it becomes part of the user's experience. @@ -429,7 +430,7 @@ first. At this point, the app **MUST** be in the `RUNNING` state. -If an app provides the `xrn:firebolt:capability:lifecycle:activatible` +If an app provides the `xrn:firebolt:capability:lifecycle:activatable` capability, then the platform **MUST** call the app's implementation of `Activity.activate()`: @@ -465,16 +466,11 @@ activation which may include: - Performing any entitlement checks to decide whether to display a player or a purchase flow - Any other steps necesary to present content to the user quickly +The platform **MAY** display a loading screen during the `activate()` callback, see +[Loading Screen](./presentation.md#5-loading-screen) for more info. -The platform will display a loading screen for the entire duration of the -`activate()` callback, and apps **SHOULD** do whatever is necessary to present -the user with content that fulfills the `intent` without additional loading -screens in the app's UX. - -**TODO**: Discuss ^^ ### 5.3. Deactivating an app - Closing an app transitions it to the `RUNNING` state, so that it is no longer part of the user's experience. @@ -518,7 +514,7 @@ remainder of this section does not apply. At this point, the app **MUST** be in the `ACTIVE` state. -If an app provides the `xrn:firebolt:capability:lifecycle:activatible` +If an app provides the `xrn:firebolt:capability:lifecycle:activatable` capability, then the platform **MUST** call the app's implementation of `Activity.deactivate()`: @@ -590,9 +586,9 @@ During the `suspend()` transition, the app: > **MUST** deallocate any graphics surface. > > **SHOULD** reduce memory usage, if possible. +> ### 5.5. Resuming an app - Resuming an app allows it to reallocate graphics composition and reload any resources it might have deallocated during `suspend()`. @@ -644,8 +640,8 @@ TBD Firebolt apps that have permission to use the `xrn:firebolt:capability:lifecycle:sleepable` capability **MUST** implement `Sleepable.wake()`. -### 5.8. Destroying an app +### 5.8. Destroying an app Destroying an app transitions it out of memory, so that it is no longer using resources on the device. @@ -703,8 +699,8 @@ type CreateParameters = { ## 7. Core SDK APIs - The following APIs are exposed by the Firebolt Core SDK. + ### 7.1. Application Interface The `Application` interface is implemented by Apps to provide resource @@ -723,8 +719,8 @@ interface Application { function resume(): Promise; function destroy(): Promsie; } - ``` + | Method | Description | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `create()` | Called when the platform is ready to create the lifecycle session for the app. Only called only once, during the `INITIALIZING` state. | @@ -732,14 +728,14 @@ interface Application { | `resume()` | Called when the platform wants the app to reallocate its graphics surface and prepare to be potentially used. | | `destroy()` | Called when the platform is ready to end the lifecycle session for the app. Only called once. | -### 7.2. Activatible Interface +### 7.2. Activatable Interface -The `Activatible` interface is implemented by Apps that provide user +The `Activatable` interface is implemented by Apps that provide user perceptible experiences, e.g. visible, audible, or user input. These types of apps require additional resource management ```typescript -interface Activatible { +interface Activatable { function activate(intent: NavigationIntent): Promise; function deactivate(): Promise; } @@ -810,11 +806,13 @@ class ExampleApplication implements Lifecycle.Application, Lifecycle.Activity { Lifecycle.provide([ "xrn:firebolt:capability:lifecycle:application", - "xrn:firebolt:capability:lifecycle:activatible" + "xrn:firebolt:capability:lifecycle:activatable" ], new ExampleApplication()) ``` +**TODO**: remove old APIs + **NOTE**: we need to support passing an array of capabilities for a single class. diff --git a/requirements/specifications/lifecycle/presentation.md b/requirements/specifications/lifecycle/presentation.md index f4163db3e..bcb4c7a79 100644 --- a/requirements/specifications/lifecycle/presentation.md +++ b/requirements/specifications/lifecycle/presentation.md @@ -1,10 +1,11 @@ # App Presentation -Document Status: Working Draft +Document Status: Candidate Specification See [Firebolt Requirements Governance](../../governance.md) for more info. | Contributor | Organization | + | ------------------------- | ------------ | | Andrew Bennett | Sky | | Cody Bonney | Charter | @@ -23,7 +24,7 @@ This document describes the requirements that Firebolt platforms and Firebolt applications must fulfill when managing App Presention. *App Presentation* refers to the display, focus, and navigational aspects of an App. -The *display* of an app refers to it its visibility and size. +The *display* of an app refers to its visibility and size. Whether an app has an *overlay* refers to any other apps or UX being presented on top of the app. @@ -36,16 +37,15 @@ intents, which are like deep links from the platform. - [1. Overview](#1-overview) - [2. Table of Contents](#2-table-of-contents) - [3. Focus](#3-focus) -- [4. Display](#4-display) - - [4.1. Display vs Lifecycle](#41-display-vs-lifecycle) -- [5. Loading Screen](#5-loading-screen) - - [5.1. Platform-provided Loading Screen](#51-platform-provided-loading-screen) - - [5.2. App-provided Loading Screen](#52-app-provided-loading-screen) - - [5.3. When to use a loading screen](#53-when-to-use-a-loading-screen) -- [6. Overlay](#6-overlay) - - [6.1. Overlay vs Focus](#61-overlay-vs-focus) -- [7. Navigation](#7-navigation) -- [8. Background Audio](#8-background-audio) +- [4. Visible](#4-visible) +- [5. Display](#5-display) + - [5.1. Display vs Lifecycle](#51-display-vs-lifecycle) +- [6. Loading Screen](#6-loading-screen) + - [6.1. Platform-provided Loading Screen](#61-platform-provided-loading-screen) + - [6.2. App-provided Loading Screen](#62-app-provided-loading-screen) +- [7. Overlay](#7-overlay) + - [7.1. Overlay vs Focus](#71-overlay-vs-focus) +- [8. Navigation](#8-navigation) - [9. Picture-in-Picture Video](#9-picture-in-picture-video) @@ -54,11 +54,20 @@ intents, which are like deep links from the platform. The `Presentation` module **MUST** have a `focus` boolean property that returns whether or not the app has input, e.g. RCU, focus. -**TODO**: let's clearly define focus. RCU, soft-keyboard, soft-remote, +An app with focus **MUST** receive key presses from the RCU. + +Firebolt platforms **MAY** decide which RCU keys are privilleged, and do not go to apps. + +Firebolt platforms **MAY** forward RCU keys to an app that does not have focus, e.g. a pause key while there's a settings overlay. + +An app **MAY** receive intents from the platform regardless of whether it has focus or not. As a property, this API also has an `onFocusChanged` notification. -## 4. Display +## 4. Visible +The `Presentation` module **MUST** have a `visibile` boolean property that returns `true` if the `display` is one of `fullscreen`, `scaled`, `thumbnail`. + +## 5. Display The `Presentation` module **MUST** have a `display` string property that returns one of the following values: @@ -66,16 +75,15 @@ returns one of the following values: | Value | Description | |--------------|-------------------------------------------------------------------------------------------------------------------------| | `fullscreen` | The app is displayed such that the dimensions fill the entire screen | -| `offscreen` | The app is has it's graphics surface attached, but not displayed on the screen at the moment, e.g. scrolled out of view | +| `offscreen` | The app has it's graphics surface attached, but not displayed on the screen at the moment, e.g. scrolled out of view | | `scaled` | The app is displayed at a size smaller than the entire screen but at least 25% of the width or height | | `thumbnail` | The app is displayed at a size smaller than 25% of the width or height of the entire screen | | `loading` | The platform is displaying a loading screen while the app prepares to be activated | -| `none` | The app does not have it's graphics surface attached to the screen | +| `none` | The app does not have its graphics surface attached to the screen | -**TODO**: an app could be offscreen & scaled. If a scaled or thumbnailed app -goes offscreen, it's now offscreen. +Note that if a scaled or thumbnailed app goes offscreen, it's now offscreen. -### 4.1. Display vs Lifecycle +### 5.1. Display vs Lifecycle Each Lifecycle state only supports certain display states: @@ -90,7 +98,7 @@ Each Lifecycle state only supports certain display states: See [Picture-in-picture](#9-picture-in-picture-video) and [Background Audio](#8-background-audio) for exceptions to this. -## 5. Loading Screen +## 6. Loading Screen In order to manage user expectations, Firebolt platforms **MAY** display loading screens to end users when an app is going to be activated. Loading Screens may be rendered either by the platform, the app, or both, depending on @@ -100,19 +108,26 @@ Firebolt platforms **MAY** decide when an app activation warrents a loading screen, for example when an app will be initialized or woken from sleep before activation. -Proposal: - If an app has the `xrn:firebolt:capability:presentation:loading-screen` capability and the platform chooses to utilize the app's loading screen, the app **MAY** be made visible any time from the beginning of activate() transition and **MUST** be made visible no later than the end. +When the app is made visbile, the platform **MUST** update the display state to one of: + +- `fullscreen` +- `scaled` +- `thumbnail` +- `offscreen` + If an app does not have the `xrn:firebolt:capability:presentation:loading-screen` capability or the platform decides not to use the app's loading screen, the app **MUST** be made visible at the end of the activate() transition. -### 5.1. Platform-provided Loading Screen +It is up to each platform to determine when a loading screen is useful. + +### 6.1. Platform-provided Loading Screen If an app provides the `xrn:firebolt:capability:presentation:loading-screen` capability, then the platform **MAY** use the app-provided loading screen, in @@ -127,8 +142,7 @@ then: > The loading screen **MUST** be displayed when the user attempts to launch the > app. > -> The loading screen **MUST** stay displayed until the app becomes active, or -> launching is cancelled. +> The loading screen **MUST** stay displayed until the app becomes active. > > The presentation state of the app **MUST** be `LOADING` for the entire time > the loading screen is displayed. @@ -138,7 +152,7 @@ then: See [Lifecycle](./index.md) for more info on launching. -### 5.2. App-provided Loading Screen +### 6.2. App-provided Loading Screen If an app provides the `xrn:firebolt:capability:presentation:loading-screen` capability, then the platform **MAY** invoke this capability in some @@ -165,18 +179,7 @@ the `activate()` transition. See [Lifecycle](./index.md) for more info on loading and activating apps. -### 5.3. When to use a loading screen - -It is up to each platform to determine when a loading screen is useful. - -Platforms **SHOULD** consider displaying a loading screen for: - -- app cold launch -- app wake from sleep - -TODO: define all the various happy path & edge cases for this. - -## 6. Overlay +## 7. Overlay The `Presentation` module **MUST** have an `overlay` string property that informs the app when a focus-consuming overlay is present. @@ -186,14 +189,14 @@ informs the app when a focus-consuming overlay is present. | `blocking` | There is a blocking overlay covering a majority of the app. | | `none` | There is nothing covering the app. | -### 6.1. Overlay vs Focus +### 7.1. Overlay vs Focus | Focus | Overlay | | ----- | ----------------------- | | true | none | | false | partial, blocking, none | -## 7. Navigation +## 8. Navigation Typically navigation is handled either when the app is activated, via the `intent` parameter of the [`activate()` method](./index.md#52-activating-an-app), or by internal input within the app. @@ -208,16 +211,34 @@ capability then the platform **MUST** call the `Navigation.navigateTo` method of the app's provider and pass an `intent` to an app that is in the `ACTIVE` state. +To invoke an app's `navigateTo` provider API the platform **MUST**: + +> The platform **MUST** dispatch the `Presentation.onRequestNavigateTo` +> notification to the app, and wait for `appNavigateToTimeout` milliseconds for +> either a `Presentation.navigateToResult` or `Presentation.navigateToError` +> call in response. +> +> Once the platform receives the `navigateToResult` call, then the platform may +> proceed with the expectation that the app in fact will handle the `intent`. +> +> If the app times out or makes an `navigateToError` call, then the app **MAY** +> have focus removed or be deactivated, so that the platform can handle the +> `intent` in some other way. + +An app **SHOULD** call `navigateToResult` after the `onRequestNavigateTo` call if the app is capabable of handling the intent. Otherwise the app **SHOULD** call `navigateToError`. + An app **MAY** receive a navigate call while it is already executing a navigate call. -An app **MUST** acknowledge receipt of each navigate call. +An app **MUST** acknowledge receipt of each navigate call with either `navigateToResult` `navigateToError`. Platforms **MAY** decide to remove focus from or deactivate apps that do not acknowledge the `navigateTo` call. -An app **MAY** decide how to prioritize multiple navigate calls, but likely -**SHOULD** prioritize the most recent one. +If an app receives multiple navigate calls in parallel, it **MAY** ignore all but the final call. + +An app can decide how to handle multiple navigate calls, but +**SHOULD** execute the final call. To fullfil a prioritized `navigateTo()` call, the app **MUST** inspect the `intent` parameter and prepare to fulfill a specific [Navigation @@ -229,33 +250,6 @@ Intent](../intents/navigation.md) which may include: The app **MAY** display a loading indicator. -To invoke an app's `navigateTo` provider API the platform **MUST**: - -> The platform **MUST** dispatch the `Presentation.onRequestNavigateTo` -> notification to the app, and wait for `appNavigateToTimeout` milliseconds for -> either a `Presentation.navigateToResult` or `Presentation.navigateToError` -> call in response. -> -> Once the platform receives the `navigateToResult` call, then the platform may -> proceed with the expectation that the app in fact will handle the `intent` -> -> If the app times out or makes an `navigateToError` call, then the app **MAY** -> have focus removed or be deactivated, so that the platform can handle the -> `intent` in some other way - - -## 8. Background Audio -If an app has the `xrn:firebolt:capability:media:background-audio`, then it can -keep playing audio/video when the app is in the `none` display state and the -audio will be played for the user. - -When an app has this capability, it **MAY** be put into the `none` display -state while in the `active` Lifecycle state. - -TODO: do we want background apps to have a gfx surface? that means they'd be in -'offscreen' display and using more memory TODO: we probably want to support -both modes here. - ## 9. Picture-in-Picture Video If an app has the `xrn:firebolt:capability:media:picture-in-picture`, then it can keep playing audio/video when the app is in the `none` display state and