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 all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,7 @@ public void onFlutterUiNoLongerDisplayed() {
}
};

private final Consumer<WindowLayoutInfo> windowInfoListener =
new Consumer<WindowLayoutInfo>() {
@Override
public void accept(WindowLayoutInfo layoutInfo) {
setWindowInfoListenerDisplayFeatures(layoutInfo);
}
};
private Consumer<WindowLayoutInfo> windowInfoListener;

/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes.
Expand Down Expand Up @@ -512,6 +506,10 @@ protected void onAttachedToWindow() {
this.windowInfoRepo = createWindowInfoRepo();
Activity activity = ViewUtils.getActivity(getContext());
if (windowInfoRepo != null && activity != null) {
// Creating windowInfoListener on-demand instead of at initialization is necessary in order to
// prevent it from capturing the wrong instance of `this` when spying for testing.
// See https://github.com/mockito/mockito/issues/3479
windowInfoListener = this::setWindowInfoListenerDisplayFeatures;
windowInfoRepo.addWindowLayoutInfoListener(
activity, ContextCompat.getMainExecutor(getContext()), windowInfoListener);
}
Expand All @@ -524,9 +522,10 @@ protected void onAttachedToWindow() {
*/
@Override
protected void onDetachedFromWindow() {
if (windowInfoRepo != null) {
if (windowInfoRepo != null && windowInfoListener != null) {
windowInfoRepo.removeWindowLayoutInfoListener(windowInfoListener);
}
windowInfoListener = null;
this.windowInfoRepo = null;
super.onDetachedFromWindow();
}
Expand All @@ -537,12 +536,12 @@ protected void onDetachedFromWindow() {
*/
@TargetApi(API_LEVELS.API_28)
protected void setWindowInfoListenerDisplayFeatures(WindowLayoutInfo layoutInfo) {
List<DisplayFeature> displayFeatures = layoutInfo.getDisplayFeatures();
List<FlutterRenderer.DisplayFeature> result = new ArrayList<>();
List<DisplayFeature> newDisplayFeatures = layoutInfo.getDisplayFeatures();
List<FlutterRenderer.DisplayFeature> flutterDisplayFeatures = new ArrayList<>();

// Data from WindowInfoTracker display features. Fold and hinge areas are
// populated here.
for (DisplayFeature displayFeature : displayFeatures) {
for (DisplayFeature displayFeature : newDisplayFeatures) {
Log.v(
TAG,
"WindowInfoTracker Display Feature reported with bounds = "
Expand All @@ -565,31 +564,17 @@ protected void setWindowInfoListenerDisplayFeatures(WindowLayoutInfo layoutInfo)
} else {
state = DisplayFeatureState.UNKNOWN;
}
result.add(new FlutterRenderer.DisplayFeature(displayFeature.getBounds(), type, state));
flutterDisplayFeatures.add(
new FlutterRenderer.DisplayFeature(displayFeature.getBounds(), type, state));
} else {
result.add(
flutterDisplayFeatures.add(
new FlutterRenderer.DisplayFeature(
displayFeature.getBounds(),
DisplayFeatureType.UNKNOWN,
DisplayFeatureState.UNKNOWN));
}
}

// Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
// populated here. DisplayCutout was introduced in API 28.
if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
WindowInsets insets = getRootWindowInsets();
if (insets != null) {
DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
for (Rect bounds : cutout.getBoundingRects()) {
Log.v(TAG, "DisplayCutout area reported with bounds = " + bounds.toString());
result.add(new FlutterRenderer.DisplayFeature(bounds, DisplayFeatureType.CUTOUT));
}
}
}
}
viewportMetrics.displayFeatures = result;
viewportMetrics.setDisplayFeatures(flutterDisplayFeatures);
sendViewportMetricsToFlutter();
}

Expand Down Expand Up @@ -782,6 +767,22 @@ navigationBarVisible && guessBottomKeyboardInset(insets) == 0
viewportMetrics.viewInsetLeft = 0;
}

// Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
// populated here. DisplayCutout was introduced in API 28.
List<FlutterRenderer.DisplayFeature> displayCutouts = new ArrayList<>();
if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
for (Rect bounds : cutout.getBoundingRects()) {
Log.v(TAG, "DisplayCutout area reported with bounds = " + bounds.toString());
displayCutouts.add(
new FlutterRenderer.DisplayFeature(
bounds, DisplayFeatureType.CUTOUT, DisplayFeatureState.UNKNOWN));
}
}
}
viewportMetrics.setDisplayCutouts(displayCutouts);

// The caption bar inset is a new addition, and the APIs called to query it utilize a list of
// bounding Rects instead of an Insets object, which is a newer API method, as compared to the
// existing Insets-based method calls above.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,13 @@ public void stopRenderingToSurface() {
}
}

private void translateFeatureBounds(int[] displayFeatureBounds, int offset, Rect bounds) {
displayFeatureBounds[offset] = bounds.left;
displayFeatureBounds[offset + 1] = bounds.top;
displayFeatureBounds[offset + 2] = bounds.right;
displayFeatureBounds[offset + 3] = bounds.bottom;
}

/**
* Notifies Flutter that the viewport metrics, e.g. window height and width, have changed.
*
Expand Down Expand Up @@ -1187,20 +1194,31 @@ public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) {
+ viewportMetrics.systemGestureInsetRight
+ "\n"
+ "Display Features: "
+ viewportMetrics.displayFeatures.size());

int[] displayFeaturesBounds = new int[viewportMetrics.displayFeatures.size() * 4];
int[] displayFeaturesType = new int[viewportMetrics.displayFeatures.size()];
int[] displayFeaturesState = new int[viewportMetrics.displayFeatures.size()];
+ viewportMetrics.displayFeatures.size()
+ "\n"
+ "Display Cutouts: "
+ viewportMetrics.displayCutouts.size());

int totalFeaturesAndCutouts =
viewportMetrics.displayFeatures.size() + viewportMetrics.displayCutouts.size();
int[] displayFeaturesBounds = new int[totalFeaturesAndCutouts * 4];
int[] displayFeaturesType = new int[totalFeaturesAndCutouts];
int[] displayFeaturesState = new int[totalFeaturesAndCutouts];
for (int i = 0; i < viewportMetrics.displayFeatures.size(); i++) {
DisplayFeature displayFeature = viewportMetrics.displayFeatures.get(i);
displayFeaturesBounds[4 * i] = displayFeature.bounds.left;
displayFeaturesBounds[4 * i + 1] = displayFeature.bounds.top;
displayFeaturesBounds[4 * i + 2] = displayFeature.bounds.right;
displayFeaturesBounds[4 * i + 3] = displayFeature.bounds.bottom;
translateFeatureBounds(displayFeaturesBounds, 4 * i, displayFeature.bounds);
displayFeaturesType[i] = displayFeature.type.encodedValue;
displayFeaturesState[i] = displayFeature.state.encodedValue;
}
int cutoutOffset = viewportMetrics.displayFeatures.size() * 4;
for (int i = 0; i < viewportMetrics.displayCutouts.size(); i++) {
DisplayFeature displayCutout = viewportMetrics.displayCutouts.get(i);
translateFeatureBounds(displayFeaturesBounds, cutoutOffset + 4 * i, displayCutout.bounds);
displayFeaturesType[viewportMetrics.displayFeatures.size() + i] =
displayCutout.type.encodedValue;
displayFeaturesState[viewportMetrics.displayFeatures.size() + i] =
displayCutout.state.encodedValue;
}

flutterJNI.setViewportMetrics(
viewportMetrics.devicePixelRatio,
Expand Down Expand Up @@ -1314,7 +1332,29 @@ boolean validate() {
return width > 0 && height > 0 && devicePixelRatio > 0;
}

public List<DisplayFeature> displayFeatures = new ArrayList<>();
// Features
private final List<DisplayFeature> displayFeatures = new ArrayList<>();

// Specifically display cutouts.
private final List<DisplayFeature> displayCutouts = new ArrayList<>();

public List<DisplayFeature> getDisplayFeatures() {
return displayFeatures;
}

public List<DisplayFeature> getDisplayCutouts() {
return displayCutouts;
}

public void setDisplayFeatures(List<DisplayFeature> newFeatures) {
displayFeatures.clear();
displayFeatures.addAll(newFeatures);
}

public void setDisplayCutouts(List<DisplayFeature> newCutouts) {
displayCutouts.clear();
displayCutouts.addAll(newCutouts);
}
}

/**
Expand All @@ -1337,12 +1377,6 @@ public DisplayFeature(Rect bounds, DisplayFeatureType type, DisplayFeatureState
this.type = type;
this.state = state;
}

public DisplayFeature(Rect bounds, DisplayFeatureType type) {
this.bounds = bounds;
this.type = type;
this.state = DisplayFeatureState.UNKNOWN;
}
}

/**
Expand Down
Loading