Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
323490e
Add DisplayFeatures and DisplayCutout to viewport metrics
andreidiaconu Jan 18, 2021
99f7bcd
WindowManager alpha03 and better documentation
andreidiaconu Mar 3, 2021
45dacf2
Merge commit '91d92787c153ec390a8725f364c544e2c3fe39a8' into foldable…
andreidiaconu Mar 3, 2021
7ee3477
Fixed formatting
andreidiaconu Mar 16, 2021
437c066
Merge commit '074463bb6afb6260e01f2b1e37ebf39ba1c9530f' into foldable…
andreidiaconu Mar 16, 2021
62a1e2b
Fixed failing tests
andreidiaconu Mar 16, 2021
357f316
Replaced API version code P with 28
andreidiaconu Mar 16, 2021
f91efa1
Unnecessary import
andreidiaconu Mar 16, 2021
e5b6188
Fixed display features parameters missing
andreidiaconu Mar 16, 2021
be2e291
Documentation changes
andreidiaconu Mar 29, 2021
84645dc
First paragraph is a sentence
andreidiaconu Mar 29, 2021
8872e39
Update androidx window library to version 1.0.0-alpha05
andreidiaconu Mar 29, 2021
284a029
Improve documentation, remove deprecated display feature call
andreidiaconu Apr 5, 2021
17fd7a6
Write tests and improve documentation
andreidiaconu Apr 7, 2021
a73cfe5
Migrate to Window Manager alpha08
andreidiaconu Jun 23, 2021
0e659cf
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Jun 23, 2021
de52872
Update androidx window to beta01
andreidiaconu Aug 20, 2021
3dc358c
Remove unused import
andreidiaconu Aug 20, 2021
3b58160
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Aug 20, 2021
5452a01
Remove window_hook_integration_test.dart
andreidiaconu Aug 20, 2021
0e6fe2a
Fix tests
andreidiaconu Aug 20, 2021
3174f12
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Aug 26, 2021
888ee95
Update android_embedding_dependencies version to include androidx.window
andreidiaconu Aug 26, 2021
d22920e
Add androidx.window to test_runner build
andreidiaconu Aug 26, 2021
3fd0699
Add empty display features arrays to fuchsia platform view
andreidiaconu Aug 27, 2021
58d3eb2
Fix tests using final class mocking
andreidiaconu Aug 28, 2021
e12f54a
Add missing license header
andreidiaconu Aug 28, 2021
afce662
Update licenses_golden
andreidiaconu Aug 28, 2021
89fe749
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Aug 31, 2021
07e2ccd
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Sep 2, 2021
20c8452
Merge branch 'master' of github.com:flutter/engine into foldable_support
andreidiaconu Sep 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ void _updateWindowMetrics(
double systemGestureInsetRight,
double systemGestureInsetBottom,
double systemGestureInsetLeft,
List<double> displayFeaturesBounds,
List<int> displayFeaturesType,
List<int> displayFeaturesState,
) {
PlatformDispatcher.instance._updateWindowMetrics(
id,
Expand All @@ -45,6 +48,9 @@ void _updateWindowMetrics(
systemGestureInsetRight,
systemGestureInsetBottom,
systemGestureInsetLeft,
displayFeaturesBounds,
displayFeaturesType,
displayFeaturesState,
);
}

Expand Down
190 changes: 190 additions & 0 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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++){
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
Expand Down Expand Up @@ -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.
Expand All @@ -1009,6 +1046,7 @@ class ViewConfiguration {
WindowPadding? viewPadding,
WindowPadding? systemGestureInsets,
WindowPadding? padding,
List<DisplayFeature>? displayFeatures,
}) {
return ViewConfiguration(
window: window ?? this.window,
Expand All @@ -1019,6 +1057,7 @@ class ViewConfiguration {
viewPadding: viewPadding ?? this.viewPadding,
systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets,
padding: padding ?? this.padding,
displayFeatures: displayFeatures ?? this.displayFeatures,
);
}

Expand Down Expand Up @@ -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
/// 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]';
Expand Down Expand Up @@ -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.
///
/// ![Device with a hinge display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_hinge.png)
///
/// ![Device with a fold display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_fold.png)
///
/// ![Device with a cutout display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_cutout.png)
///
/// 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,
}) : 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.
///
/// For cutouts, this is [DisplayFeatureState.unknown]
final DisplayFeatureState state;

@override
bool operator ==(Object other) {
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.
///
/// ![Device with a hinge display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_hinge.png)
///
/// ![Device with a fold display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_fold.png)
///
/// ![Device with a cutout display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_cutout.png)
enum DisplayFeatureType {
/// [DisplayFeature] type is new and not yet known to Flutter.
unknown,
/// 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.
cutout,
}

/// 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
/// [Android Postures](https://developer.android.com/guide/topics/ui/foldables#postures).
Copy link
Contributor

Choose a reason for hiding this comment

The 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 unknown are posture<blah>, might this be better called DisplayFeaturePosture instead of DisplayFeatureState?

I'll leave the choice up to you.

Copy link
Contributor Author

@andreidiaconu andreidiaconu Aug 26, 2021

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense. I'm ok with state.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 {
/// 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.
postureHalfOpened,
/// The foldable device is flipped with the flexible screen parts or physical
/// screens facing opposite directions.
postureFlipped,
}

/// An identifier used to select a user's language and formatting preferences.
///
/// This represents a [Unicode Language
Expand Down
11 changes: 11 additions & 0 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ abstract class FlutterView {
/// applications.
WindowPadding get padding => viewConfiguration.padding;

/// {@macro dart.ui.ViewConfiguration.displayFeatures}
///
/// When this changes, [onMetricsChanged] is called.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this value changes.
/// * [MediaQuery.of], a simpler mechanism to access this data.
List<DisplayFeature> get displayFeatures => viewConfiguration.displayFeatures;

/// Updates the view's rendering on the GPU with the newly provided [Scene].
///
/// This function must be called within the scope of the
Expand Down
49 changes: 30 additions & 19 deletions lib/ui/window/viewport_metrics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,25 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio,
physical_width(p_physical_width),
physical_height(p_physical_height) {}

ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio,
double p_physical_width,
double p_physical_height,
double p_physical_padding_top,
double p_physical_padding_right,
double p_physical_padding_bottom,
double p_physical_padding_left,
double p_physical_view_inset_top,
double p_physical_view_inset_right,
double p_physical_view_inset_bottom,
double p_physical_view_inset_left,
double p_physical_system_gesture_inset_top,
double p_physical_system_gesture_inset_right,
double p_physical_system_gesture_inset_bottom,
double p_physical_system_gesture_inset_left)
ViewportMetrics::ViewportMetrics(
double p_device_pixel_ratio,
double p_physical_width,
double p_physical_height,
double p_physical_padding_top,
double p_physical_padding_right,
double p_physical_padding_bottom,
double p_physical_padding_left,
double p_physical_view_inset_top,
double p_physical_view_inset_right,
double p_physical_view_inset_bottom,
double p_physical_view_inset_left,
double p_physical_system_gesture_inset_top,
double p_physical_system_gesture_inset_right,
double p_physical_system_gesture_inset_bottom,
double p_physical_system_gesture_inset_left,
const std::vector<double> p_physical_display_features_bounds,
const std::vector<int> p_physical_display_features_type,
const std::vector<int> p_physical_display_features_state)
: device_pixel_ratio(p_device_pixel_ratio),
physical_width(p_physical_width),
physical_height(p_physical_height),
Expand All @@ -48,8 +52,10 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio,
p_physical_system_gesture_inset_right),
physical_system_gesture_inset_bottom(
p_physical_system_gesture_inset_bottom),
physical_system_gesture_inset_left(p_physical_system_gesture_inset_left) {
}
physical_system_gesture_inset_left(p_physical_system_gesture_inset_left),
physical_display_features_bounds(p_physical_display_features_bounds),
physical_display_features_type(p_physical_display_features_type),
physical_display_features_state(p_physical_display_features_state) {}

bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) {
return a.device_pixel_ratio == b.device_pixel_ratio &&
Expand All @@ -70,7 +76,11 @@ bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) {
a.physical_system_gesture_inset_bottom ==
b.physical_system_gesture_inset_bottom &&
a.physical_system_gesture_inset_left ==
b.physical_system_gesture_inset_left;
b.physical_system_gesture_inset_left &&
a.physical_display_features_bounds ==
b.physical_display_features_bounds &&
a.physical_display_features_type == b.physical_display_features_type &&
a.physical_display_features_state == b.physical_display_features_state;
}

std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) {
Expand All @@ -85,7 +95,8 @@ std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) {
<< "Gesture Insets: [" << a.physical_system_gesture_inset_top << "T "
<< a.physical_system_gesture_inset_right << "R "
<< a.physical_system_gesture_inset_bottom << "B "
<< a.physical_system_gesture_inset_left << "L]";
<< a.physical_system_gesture_inset_left << "L] "
<< "Display Features size: " << a.physical_display_features_type.size();
return os;
}

Expand Down
9 changes: 8 additions & 1 deletion lib/ui/window/viewport_metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_

#include <ostream>
#include <vector>

namespace flutter {

Expand All @@ -28,7 +29,10 @@ struct ViewportMetrics {
double p_physical_system_gesture_inset_top,
double p_physical_system_gesture_inset_right,
double p_physical_system_gesture_inset_bottom,
double p_physical_system_gesture_inset_left);
double p_physical_system_gesture_inset_left,
const std::vector<double> p_physical_display_features_bounds,
const std::vector<int> p_physical_display_features_type,
const std::vector<int> p_physical_display_features_state);

double device_pixel_ratio = 1.0;
double physical_width = 0;
Expand All @@ -45,6 +49,9 @@ struct ViewportMetrics {
double physical_system_gesture_inset_right = 0;
double physical_system_gesture_inset_bottom = 0;
double physical_system_gesture_inset_left = 0;
std::vector<double> physical_display_features_bounds;
std::vector<int> physical_display_features_type;
std::vector<int> physical_display_features_state;
};

bool operator==(const ViewportMetrics& a, const ViewportMetrics& b);
Expand Down
3 changes: 3 additions & 0 deletions lib/ui/window/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) {
tonic::ToDart(metrics.physical_system_gesture_inset_right),
tonic::ToDart(metrics.physical_system_gesture_inset_bottom),
tonic::ToDart(metrics.physical_system_gesture_inset_left),
tonic::ToDart(metrics.physical_display_features_bounds),
tonic::ToDart(metrics.physical_display_features_type),
tonic::ToDart(metrics.physical_display_features_state),
}));
}

Expand Down
Loading