Skip to content

Commit c348791

Browse files
yaakovschectmanGitHub Actions Bot
authored andcommitted
Move detection of cutouts in Android engine to onApplyWindowInsets (flutter#55992)
Stop including the translation of window insets cutouts to Flutter cutout DisplayFeatures in the window info listener method, which would not necessarily receive changes in the correct order/state when they update, e.g. when the screen orientation changes. Fixes flutter/flutter#155658 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent af0f0d5 commit c348791

File tree

4 files changed

+251
-75
lines changed

4 files changed

+251
-75
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterView.java

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,7 @@ public void onFlutterUiNoLongerDisplayed() {
194194
}
195195
};
196196

197-
private final Consumer<WindowLayoutInfo> windowInfoListener =
198-
new Consumer<WindowLayoutInfo>() {
199-
@Override
200-
public void accept(WindowLayoutInfo layoutInfo) {
201-
setWindowInfoListenerDisplayFeatures(layoutInfo);
202-
}
203-
};
197+
private Consumer<WindowLayoutInfo> windowInfoListener;
204198

205199
/**
206200
* Constructs a {@code FlutterView} programmatically, without any XML attributes.
@@ -512,6 +506,10 @@ protected void onAttachedToWindow() {
512506
this.windowInfoRepo = createWindowInfoRepo();
513507
Activity activity = ViewUtils.getActivity(getContext());
514508
if (windowInfoRepo != null && activity != null) {
509+
// Creating windowInfoListener on-demand instead of at initialization is necessary in order to
510+
// prevent it from capturing the wrong instance of `this` when spying for testing.
511+
// See https://github.com/mockito/mockito/issues/3479
512+
windowInfoListener = this::setWindowInfoListenerDisplayFeatures;
515513
windowInfoRepo.addWindowLayoutInfoListener(
516514
activity, ContextCompat.getMainExecutor(getContext()), windowInfoListener);
517515
}
@@ -524,9 +522,10 @@ protected void onAttachedToWindow() {
524522
*/
525523
@Override
526524
protected void onDetachedFromWindow() {
527-
if (windowInfoRepo != null) {
525+
if (windowInfoRepo != null && windowInfoListener != null) {
528526
windowInfoRepo.removeWindowLayoutInfoListener(windowInfoListener);
529527
}
528+
windowInfoListener = null;
530529
this.windowInfoRepo = null;
531530
super.onDetachedFromWindow();
532531
}
@@ -537,12 +536,12 @@ protected void onDetachedFromWindow() {
537536
*/
538537
@TargetApi(API_LEVELS.API_28)
539538
protected void setWindowInfoListenerDisplayFeatures(WindowLayoutInfo layoutInfo) {
540-
List<DisplayFeature> displayFeatures = layoutInfo.getDisplayFeatures();
541-
List<FlutterRenderer.DisplayFeature> result = new ArrayList<>();
539+
List<DisplayFeature> newDisplayFeatures = layoutInfo.getDisplayFeatures();
540+
List<FlutterRenderer.DisplayFeature> flutterDisplayFeatures = new ArrayList<>();
542541

543542
// Data from WindowInfoTracker display features. Fold and hinge areas are
544543
// populated here.
545-
for (DisplayFeature displayFeature : displayFeatures) {
544+
for (DisplayFeature displayFeature : newDisplayFeatures) {
546545
Log.v(
547546
TAG,
548547
"WindowInfoTracker Display Feature reported with bounds = "
@@ -565,31 +564,17 @@ protected void setWindowInfoListenerDisplayFeatures(WindowLayoutInfo layoutInfo)
565564
} else {
566565
state = DisplayFeatureState.UNKNOWN;
567566
}
568-
result.add(new FlutterRenderer.DisplayFeature(displayFeature.getBounds(), type, state));
567+
flutterDisplayFeatures.add(
568+
new FlutterRenderer.DisplayFeature(displayFeature.getBounds(), type, state));
569569
} else {
570-
result.add(
570+
flutterDisplayFeatures.add(
571571
new FlutterRenderer.DisplayFeature(
572572
displayFeature.getBounds(),
573573
DisplayFeatureType.UNKNOWN,
574574
DisplayFeatureState.UNKNOWN));
575575
}
576576
}
577-
578-
// Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
579-
// populated here. DisplayCutout was introduced in API 28.
580-
if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
581-
WindowInsets insets = getRootWindowInsets();
582-
if (insets != null) {
583-
DisplayCutout cutout = insets.getDisplayCutout();
584-
if (cutout != null) {
585-
for (Rect bounds : cutout.getBoundingRects()) {
586-
Log.v(TAG, "DisplayCutout area reported with bounds = " + bounds.toString());
587-
result.add(new FlutterRenderer.DisplayFeature(bounds, DisplayFeatureType.CUTOUT));
588-
}
589-
}
590-
}
591-
}
592-
viewportMetrics.displayFeatures = result;
577+
viewportMetrics.setDisplayFeatures(flutterDisplayFeatures);
593578
sendViewportMetricsToFlutter();
594579
}
595580

@@ -782,6 +767,22 @@ navigationBarVisible && guessBottomKeyboardInset(insets) == 0
782767
viewportMetrics.viewInsetLeft = 0;
783768
}
784769

770+
// Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
771+
// populated here. DisplayCutout was introduced in API 28.
772+
List<FlutterRenderer.DisplayFeature> displayCutouts = new ArrayList<>();
773+
if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
774+
DisplayCutout cutout = insets.getDisplayCutout();
775+
if (cutout != null) {
776+
for (Rect bounds : cutout.getBoundingRects()) {
777+
Log.v(TAG, "DisplayCutout area reported with bounds = " + bounds.toString());
778+
displayCutouts.add(
779+
new FlutterRenderer.DisplayFeature(
780+
bounds, DisplayFeatureType.CUTOUT, DisplayFeatureState.UNKNOWN));
781+
}
782+
}
783+
}
784+
viewportMetrics.setDisplayCutouts(displayCutouts);
785+
785786
// The caption bar inset is a new addition, and the APIs called to query it utilize a list of
786787
// bounding Rects instead of an Insets object, which is a newer API method, as compared to the
787788
// existing Insets-based method calls above.

shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,13 @@ public void stopRenderingToSurface() {
11371137
}
11381138
}
11391139

1140+
private void translateFeatureBounds(int[] displayFeatureBounds, int offset, Rect bounds) {
1141+
displayFeatureBounds[offset] = bounds.left;
1142+
displayFeatureBounds[offset + 1] = bounds.top;
1143+
displayFeatureBounds[offset + 2] = bounds.right;
1144+
displayFeatureBounds[offset + 3] = bounds.bottom;
1145+
}
1146+
11401147
/**
11411148
* Notifies Flutter that the viewport metrics, e.g. window height and width, have changed.
11421149
*
@@ -1187,20 +1194,31 @@ public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) {
11871194
+ viewportMetrics.systemGestureInsetRight
11881195
+ "\n"
11891196
+ "Display Features: "
1190-
+ viewportMetrics.displayFeatures.size());
1191-
1192-
int[] displayFeaturesBounds = new int[viewportMetrics.displayFeatures.size() * 4];
1193-
int[] displayFeaturesType = new int[viewportMetrics.displayFeatures.size()];
1194-
int[] displayFeaturesState = new int[viewportMetrics.displayFeatures.size()];
1197+
+ viewportMetrics.displayFeatures.size()
1198+
+ "\n"
1199+
+ "Display Cutouts: "
1200+
+ viewportMetrics.displayCutouts.size());
1201+
1202+
int totalFeaturesAndCutouts =
1203+
viewportMetrics.displayFeatures.size() + viewportMetrics.displayCutouts.size();
1204+
int[] displayFeaturesBounds = new int[totalFeaturesAndCutouts * 4];
1205+
int[] displayFeaturesType = new int[totalFeaturesAndCutouts];
1206+
int[] displayFeaturesState = new int[totalFeaturesAndCutouts];
11951207
for (int i = 0; i < viewportMetrics.displayFeatures.size(); i++) {
11961208
DisplayFeature displayFeature = viewportMetrics.displayFeatures.get(i);
1197-
displayFeaturesBounds[4 * i] = displayFeature.bounds.left;
1198-
displayFeaturesBounds[4 * i + 1] = displayFeature.bounds.top;
1199-
displayFeaturesBounds[4 * i + 2] = displayFeature.bounds.right;
1200-
displayFeaturesBounds[4 * i + 3] = displayFeature.bounds.bottom;
1209+
translateFeatureBounds(displayFeaturesBounds, 4 * i, displayFeature.bounds);
12011210
displayFeaturesType[i] = displayFeature.type.encodedValue;
12021211
displayFeaturesState[i] = displayFeature.state.encodedValue;
12031212
}
1213+
int cutoutOffset = viewportMetrics.displayFeatures.size() * 4;
1214+
for (int i = 0; i < viewportMetrics.displayCutouts.size(); i++) {
1215+
DisplayFeature displayCutout = viewportMetrics.displayCutouts.get(i);
1216+
translateFeatureBounds(displayFeaturesBounds, cutoutOffset + 4 * i, displayCutout.bounds);
1217+
displayFeaturesType[viewportMetrics.displayFeatures.size() + i] =
1218+
displayCutout.type.encodedValue;
1219+
displayFeaturesState[viewportMetrics.displayFeatures.size() + i] =
1220+
displayCutout.state.encodedValue;
1221+
}
12041222

12051223
flutterJNI.setViewportMetrics(
12061224
viewportMetrics.devicePixelRatio,
@@ -1314,7 +1332,29 @@ boolean validate() {
13141332
return width > 0 && height > 0 && devicePixelRatio > 0;
13151333
}
13161334

1317-
public List<DisplayFeature> displayFeatures = new ArrayList<>();
1335+
// Features
1336+
private final List<DisplayFeature> displayFeatures = new ArrayList<>();
1337+
1338+
// Specifically display cutouts.
1339+
private final List<DisplayFeature> displayCutouts = new ArrayList<>();
1340+
1341+
public List<DisplayFeature> getDisplayFeatures() {
1342+
return displayFeatures;
1343+
}
1344+
1345+
public List<DisplayFeature> getDisplayCutouts() {
1346+
return displayCutouts;
1347+
}
1348+
1349+
public void setDisplayFeatures(List<DisplayFeature> newFeatures) {
1350+
displayFeatures.clear();
1351+
displayFeatures.addAll(newFeatures);
1352+
}
1353+
1354+
public void setDisplayCutouts(List<DisplayFeature> newCutouts) {
1355+
displayCutouts.clear();
1356+
displayCutouts.addAll(newCutouts);
1357+
}
13181358
}
13191359

13201360
/**
@@ -1337,12 +1377,6 @@ public DisplayFeature(Rect bounds, DisplayFeatureType type, DisplayFeatureState
13371377
this.type = type;
13381378
this.state = state;
13391379
}
1340-
1341-
public DisplayFeature(Rect bounds, DisplayFeatureType type) {
1342-
this.bounds = bounds;
1343-
this.type = type;
1344-
this.state = DisplayFeatureState.UNKNOWN;
1345-
}
13461380
}
13471381

13481382
/**

0 commit comments

Comments
 (0)