-
Notifications
You must be signed in to change notification settings - Fork 6k
Display Features support (Foldable and Cutout) #24756
Changes from 13 commits
323490e
99f7bcd
45dacf2
7ee3477
437c066
62a1e2b
357f316
f91efa1
e5b6188
be2e291
84645dc
8872e39
284a029
17fd7a6
a73cfe5
0e659cf
de52872
3dc358c
3b58160
5452a01
0e6fe2a
3174f12
888ee95
d22920e
3fd0699
58d3eb2
e12f54a
afce662
89fe749
07e2ccd
20c8452
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,6 +176,9 @@ class PlatformDispatcher { | |
| double systemGestureInsetRight, | ||
| double systemGestureInsetBottom, | ||
| double systemGestureInsetLeft, | ||
| List<double> displayFeaturesBounds, | ||
| List<int> displayFeaturesType, | ||
| List<int> displayFeaturesState, | ||
| ) { | ||
| final ViewConfiguration previousConfiguration = | ||
| _viewConfigurations[id] ?? const ViewConfiguration(); | ||
|
|
@@ -210,10 +213,43 @@ class PlatformDispatcher { | |
| bottom: math.max(0.0, systemGestureInsetBottom), | ||
| left: math.max(0.0, systemGestureInsetLeft), | ||
| ), | ||
| displayFeatures: _decodeDisplayFeatures( | ||
| bounds: displayFeaturesBounds, | ||
| type: displayFeaturesType, | ||
| state: displayFeaturesState, | ||
| devicePixelRatio: devicePixelRatio, | ||
| ), | ||
| ); | ||
| _invoke(onMetricsChanged, _onMetricsChangedZone); | ||
| } | ||
|
|
||
| List<DisplayFeature> _decodeDisplayFeatures({ | ||
| required List<double> bounds, | ||
| required List<int> type, | ||
| required List<int> state, | ||
| required double devicePixelRatio, | ||
| }) { | ||
| assert(bounds.length / 4 == type.length); | ||
| assert(type.length == state.length); | ||
| final List<DisplayFeature> result = <DisplayFeature>[]; | ||
| for(int i = 0 ; i < type.length; i++){ | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| final int rectOffset = i * 4; | ||
| result.add(DisplayFeature( | ||
| bounds: Rect.fromLTRB( | ||
| bounds[rectOffset] / devicePixelRatio, | ||
| bounds[rectOffset + 1] / devicePixelRatio, | ||
| bounds[rectOffset + 2] / devicePixelRatio, | ||
| bounds[rectOffset + 3] / devicePixelRatio, | ||
| ), | ||
| type: DisplayFeatureType.values[type[i]], | ||
| state: state[i] < DisplayFeatureState.values.length | ||
| ? DisplayFeatureState.values[state[i]] | ||
| : DisplayFeatureState.unknown, | ||
| )); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /// A callback invoked when any view begins a frame. | ||
| /// | ||
| /// A callback that is invoked to notify the application that it is an | ||
|
|
@@ -997,6 +1033,7 @@ class ViewConfiguration { | |
| this.viewPadding = WindowPadding.zero, | ||
| this.systemGestureInsets = WindowPadding.zero, | ||
| this.padding = WindowPadding.zero, | ||
| this.displayFeatures = const <DisplayFeature>[], | ||
| }); | ||
|
|
||
| /// Copy this configuration with some fields replaced. | ||
|
|
@@ -1009,6 +1046,7 @@ class ViewConfiguration { | |
| WindowPadding? viewPadding, | ||
| WindowPadding? systemGestureInsets, | ||
| WindowPadding? padding, | ||
| List<DisplayFeature>? displayFeatures, | ||
| }) { | ||
| return ViewConfiguration( | ||
| window: window ?? this.window, | ||
|
|
@@ -1019,6 +1057,7 @@ class ViewConfiguration { | |
| viewPadding: viewPadding ?? this.viewPadding, | ||
| systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, | ||
| padding: padding ?? this.padding, | ||
| displayFeatures: displayFeatures ?? this.displayFeatures, | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -1091,6 +1130,27 @@ class ViewConfiguration { | |
| /// phone sensor housings). | ||
| final WindowPadding padding; | ||
|
|
||
|
|
||
| /// {@template dart.ui.ViewConfiguration.displayFeatures} | ||
| /// Areas of the display that are obstructed by hardware features. | ||
| /// | ||
| /// This list is populated only on Android. If the device has no display | ||
| /// features, this list is empty. | ||
| /// | ||
| /// The coordinate space in which the [DisplayFeature.bounds] are defined includes all screens | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// and the space between them. This means that the space between the screens | ||
| /// is virtually part of the Flutter view space, with the [DisplayFeature.bounds] | ||
| /// of the display feature as an obstructed area. The [DisplayFeature.type] can | ||
| /// be used to determine if this display feature obstructs the screen or not. | ||
| /// For example, [DisplayFeatureType.hinge] and [DisplayFeatureType.cutout] both | ||
| /// obstruct the display, while [DisplayFeatureType.fold] is a crease in the display. | ||
| /// | ||
| /// Folding [DisplayFeature]s like the [DisplayFeatureType.hinge] and | ||
| /// [DisplayFeatureType.fold] also have a [DisplayFeature.state] which can be | ||
| /// used to determine the posture the device is in. | ||
| /// {@endtemplate} | ||
| final List<DisplayFeature> displayFeatures; | ||
|
|
||
| @override | ||
| String toString() { | ||
| return '$runtimeType[window: $window, geometry: $geometry]'; | ||
|
|
@@ -1314,6 +1374,136 @@ class WindowPadding { | |
| } | ||
| } | ||
|
|
||
| /// Area of the display that may be obstructed by a hardware feature. | ||
| /// | ||
| /// This is populated only on Android. | ||
| /// | ||
| /// The [bounds] are measured in logical pixels. On devices with two screens the | ||
| /// coordinate system starts with [0,0] in the top-left corner of the left or top screen | ||
| /// and expands to include both screens and the visual space between them. | ||
| /// | ||
| /// The [type] describes the behaviour and if [DisplayFeature] obstructs the display. | ||
| /// For example, [DisplayFeatureType.hinge] and [DisplayFeatureType.cutout] both obstruct the display, | ||
| /// while [DisplayFeatureType.fold] does not. | ||
| /// | ||
| ///  | ||
| /// | ||
| ///  | ||
| /// | ||
| ///  | ||
| /// | ||
| /// The [state] contains information about the posture for foldable features | ||
| /// ([DisplayFeatureType.hinge] and [DisplayFeatureType.fold]). The posture is | ||
| /// the shape of the display, for example [DisplayFeatureState.postureFlat] or | ||
| /// [DisplayFeatureState.postureHalfOpened]. For [DisplayFeatureType.cutout], | ||
| /// the state is not used and has the [DisplayFeatureState.unknown] value. | ||
| class DisplayFeature { | ||
| const DisplayFeature({ | ||
| required this.bounds, | ||
| required this.type, | ||
| required this.state, | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) : assert(type != DisplayFeatureType.cutout || state == DisplayFeatureState.unknown); | ||
|
|
||
| /// The area of the flutter view occupied by this display feature, measured in logical pixels. | ||
| /// | ||
| /// On devices with two screens, the Flutter view spans from the top-left corner | ||
| /// of the left or top screen to the bottom-right corner of the right or bottom screen, | ||
| /// including the visual area occupied by any display feature. Bounds of display | ||
| /// features are reported in this coordinate system. | ||
| /// | ||
| /// For example, on a dual screen device in portrait mode: | ||
| /// | ||
| /// * [bounds.left] gives you the size of left screen, in logical pixels. | ||
| /// * [bounds.right] gives you the size of the left screen + the hinge width. | ||
| final Rect bounds; | ||
|
|
||
| /// Type of display feature, e.g. hinge, fold, cutout. | ||
| final DisplayFeatureType type; | ||
|
|
||
| /// Posture of display feature, which is populated only for folds and hinges. | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// | ||
| /// For cutouts, this is [DisplayFeatureState.unknown] | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| final DisplayFeatureState state; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) { | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (identical(this, other)) | ||
| return true; | ||
| if (other.runtimeType != runtimeType) | ||
| return false; | ||
| return other is DisplayFeature && bounds == other.bounds && | ||
| type == other.type && state == other.state; | ||
| } | ||
|
|
||
| @override | ||
| int get hashCode => hashValues(bounds, type, state); | ||
|
|
||
| @override | ||
| String toString() { | ||
| return 'DisplayFeature(rect: $bounds, type: $type, state: $state)'; | ||
| } | ||
| } | ||
|
|
||
| /// Type of [DisplayFeature], describing the [DisplayFeature] behaviour and if | ||
| /// it obstructs the display. | ||
| /// | ||
| /// Some types of [DisplayFeature], like [DisplayFeatureType.fold], can be | ||
| /// reported without actually impeding drawing on the screen. They are useful | ||
| /// for knowing where the display is bent or has a crease. The | ||
| /// [DisplayFeature.bounds] can be 0-width in such cases. | ||
| /// | ||
| /// The shape formed by the screens for types [DisplayFeatureType.fold] and | ||
| /// [DisplayFeatureType.hinge] is called the posture and is exposed in | ||
| /// [DisplayFeature.state]. For example, the [DisplayFeatureState.postureFlat] posture | ||
| /// means the screens form a flat surface, while [DisplayFeatureState.postureFlipped] | ||
| /// posture means the screens are facing opposite directions. | ||
| /// | ||
| ///  | ||
| /// | ||
| ///  | ||
| /// | ||
| ///  | ||
| enum DisplayFeatureType { | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// [DisplayFeature] type is new and not yet known to Flutter. | ||
| unknown, | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// A fold in the flexible screen without a physical gap. | ||
| /// | ||
| /// The bounds for this display feature type indicate where the display makes a crease. | ||
| fold, | ||
| /// A physical separation with a hinge that allows two display panels to fold. | ||
| hinge, | ||
| /// A non-functional area of the screen, usually housing cameras or sensors. | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| cutout, | ||
| } | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// State of the display feature, which contains information about the posture | ||
| /// for foldable features. | ||
| /// | ||
| /// The posture is the shape made by the parts of the flexible screen or | ||
| /// physical screen panels. Postures correspond to values found in | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// [Android Postures](https://developer.android.com/guide/topics/ui/foldables#postures). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Naming consideration: Since this is based off of android "Postures" and all states but I'll leave the choice up to you.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the future, some Display Features might have states that are not related to postures, so I chose this naming to allow for that. Considering this, do you have the same perspective?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that makes sense. I'm ok with state.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flexibility is a worthwhile tradeoff here for a slightly more compact naming scheme here, especially considering who knows what people may come up with in future :) |
||
| /// | ||
| /// * For [DisplayFeatureType.fold]s & [DisplayFeatureType.hinge]s, the state is | ||
| /// the posture. | ||
| /// * For [DisplayFeatureType.cutout]s, the state is not used and has the | ||
| /// [DisplayFeatureState.unknown] value. | ||
| enum DisplayFeatureState { | ||
andreidiaconu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// The display feature is a [DisplayFeatureType.cutout] or this state is new | ||
| /// and not yet known to Flutter. | ||
| unknown, | ||
| /// The foldable device is completely open. | ||
| /// | ||
| /// The screen space that is presented to the user is flat. | ||
| postureFlat, | ||
| /// Fold angle is in an intermediate position between opened and closed state. | ||
| /// | ||
| /// There is a non-flat angle between parts of the flexible screen or between physical screen panels. | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| postureHalfOpened, | ||
| /// The foldable device is flipped with the flexible screen parts or physical | ||
| /// screens facing opposite directions. | ||
andreidiaconu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| postureFlipped, | ||
| } | ||
|
|
||
| /// An identifier used to select a user's language and formatting preferences. | ||
| /// | ||
| /// This represents a [Unicode Language | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.