Always contain window frames in window bounds
Previously, if the window bounds and the safe area of display cutout
don't intersect, we might get invalid window frames.
This CL uses a new method to intersect rectangles which makes sure the
result frame is contained by the window bounds.
Fix: 367461591
Flag: EXEMPT bugfix
Test: atest WindowLayoutTests
Change-Id: I63d28e5dcdcea9f4206f5026c00905a5cb413bd3
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index dda3993..d5ccca9 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -157,10 +157,10 @@
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
mTempRect.set(outParentFrame);
- outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+ intersectOrClamp(outParentFrame, displayCutoutSafeExceptMaybeBars);
frames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
}
- outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+ intersectOrClamp(outDisplayFrame, displayCutoutSafeExceptMaybeBars);
}
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
@@ -283,6 +283,19 @@
+ " requestedInvisibleTypes=" + WindowInsets.Type.toString(~requestedVisibleTypes));
}
+ /**
+ * If both rectangles intersect, set inOutRect to that intersection. Otherwise, clamp inOutRect
+ * to the side (or the corner) that the other rectangle is away from.
+ * Unlike {@link Rect#intersectUnchecked(Rect)}, this method guarantees that the new rectangle
+ * is valid and contained in inOutRect if rectangles involved are valid.
+ */
+ private static void intersectOrClamp(Rect inOutRect, Rect other) {
+ inOutRect.left = Math.min(Math.max(inOutRect.left, other.left), inOutRect.right);
+ inOutRect.top = Math.min(Math.max(inOutRect.top, other.top), inOutRect.bottom);
+ inOutRect.right = Math.max(Math.min(inOutRect.right, other.right), inOutRect.left);
+ inOutRect.bottom = Math.max(Math.min(inOutRect.bottom, other.bottom), inOutRect.top);
+ }
+
public static void extendFrameByCutout(Rect displayCutoutSafe,
Rect displayFrame, Rect inOutFrame, Rect tempRect) {
if (displayCutoutSafe.contains(inOutFrame)) {
diff --git a/core/tests/coretests/src/android/view/WindowLayoutTests.java b/core/tests/coretests/src/android/view/WindowLayoutTests.java
index 5cac98d..d4693e6 100644
--- a/core/tests/coretests/src/android/view/WindowLayoutTests.java
+++ b/core/tests/coretests/src/android/view/WindowLayoutTests.java
@@ -413,4 +413,19 @@
assertInsetByTopBottom(0, 0, mFrames.parentFrame);
assertInsetByTopBottom(0, 0, mFrames.frame);
}
+
+ @Test
+ public void windowBoundsOutsideDisplayCutoutSafe() {
+ addDisplayCutout();
+ mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ mWindowBounds.set(0, -1000, DISPLAY_WIDTH, 0);
+ computeFrames();
+
+ assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+ mFrames.displayFrame);
+ assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+ mFrames.parentFrame);
+ assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+ mFrames.frame);
+ }
}