diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index feb17f54acd67..03f05f2468ae8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,6 +76,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; @@ -2461,8 +2462,9 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize + ", desiredWindowWidth=" + desiredWindowWidth); if (baseSize != 0 && desiredWindowWidth > baseSize) { - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height, + lp.privateFlags); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured (" + host.getMeasuredWidth() + "," + host.getMeasuredHeight() @@ -2475,7 +2477,7 @@ public final class ViewRootImpl implements ViewParent, baseSize = (baseSize+desiredWindowWidth)/2; if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize=" + baseSize); - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured (" + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); @@ -2488,8 +2490,10 @@ public final class ViewRootImpl implements ViewParent, } if (!goodMeasure) { - childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width, + lp.privateFlags); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height, + lp.privateFlags); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { windowSizeMayChange = true; @@ -3149,8 +3153,10 @@ public final class ViewRootImpl implements ViewParent, if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || dispatchApplyInsets || updatedConfiguration) { - int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); - int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); + int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width, + lp.privateFlags); + int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height, + lp.privateFlags); if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" + mWidth + " measuredWidth=" + host.getMeasuredWidth() @@ -3950,31 +3956,28 @@ public final class ViewRootImpl implements ViewParent, * Figures out the measure spec for the root view in a window based on it's * layout params. * - * @param windowSize - * The available width or height of the window - * - * @param rootDimension - * The layout params for one dimension (width or height) of the - * window. - * + * @param windowSize The available width or height of the window. + * @param measurement The layout width or height requested in the layout params. + * @param privateFlags The private flags in the layout params of the window. * @return The measure spec to use to measure the root view. */ - private static int getRootMeasureSpec(int windowSize, int rootDimension) { + private static int getRootMeasureSpec(int windowSize, int measurement, int privateFlags) { int measureSpec; + final int rootDimension = (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0 + ? MATCH_PARENT : measurement; switch (rootDimension) { - - case ViewGroup.LayoutParams.MATCH_PARENT: - // Window can't resize. Force root view to be windowSize. - measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); - break; - case ViewGroup.LayoutParams.WRAP_CONTENT: - // Window can resize. Set max size for root view. - measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); - break; - default: - // Window wants to be an exact size. Force root view to be that size. - measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); - break; + case ViewGroup.LayoutParams.MATCH_PARENT: + // Window can't resize. Force root view to be windowSize. + measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); + break; + case ViewGroup.LayoutParams.WRAP_CONTENT: + // Window can resize. Set max size for root view. + measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); + break; + default: + // Window wants to be an exact size. Force root view to be that size. + measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); + break; } return measureSpec; } diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java index 27f89ec4ac79e..ad9f21b4fa70d 100644 --- a/core/java/android/view/WindowLayout.java +++ b/core/java/android/view/WindowLayout.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.Gravity.DISPLAY_CLIP_HORIZONTAL; +import static android.view.Gravity.DISPLAY_CLIP_VERTICAL; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -29,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; @@ -250,11 +253,39 @@ public class WindowLayout { Gravity.apply(attrs.gravity, w, h, outParentFrame, (int) (x + attrs.horizontalMargin * pw), (int) (y + attrs.verticalMargin * ph), outFrame); + // Now make sure the window fits in the overall display frame. if (fitToDisplay) { Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame); } + if ((attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0 + && !cutout.isEmpty()) { + // If the actual frame covering a display cutout, and the window is requesting to extend + // it's requested frame, re-do the frame calculation after getting the new requested + // size. + mTempRect.set(outFrame); + // Do nothing if the display cutout and window don't overlap entirely. This may happen + // when the cutout is not on the same side with the window. + boolean shouldExpand = false; + final Rect [] cutoutBounds = cutout.getBoundingRectsAll(); + for (Rect cutoutBound : cutoutBounds) { + if (cutoutBound.isEmpty()) continue; + if (mTempRect.contains(cutoutBound) || cutoutBound.contains(mTempRect)) { + shouldExpand = true; + break; + } + } + if (shouldExpand) { + // Try to fit move the bar to avoid the display cutout first. Make sure the clip + // flags are not set to make the window move. + final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL; + Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe, + mTempRect); + outFrame.union(mTempRect); + } + } + if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle() + " outFrame=" + outFrame.toShortString() + " outParentFrame=" + outParentFrame.toShortString() diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8ee3e432cbbb9..dbfae46a6e724 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2384,6 +2384,16 @@ public interface WindowManager extends ViewManager { */ public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 0x00001000; + /** + * Flag to indicate that the window frame should be the requested frame adding the display + * cutout frame. This will only be applied if a specific size smaller than the parent frame + * is given, and the window is covering the display cutout. The extended frame will not be + * larger than the parent frame. + * + * {@hide} + */ + public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 0x00002000; + /** * Flag that will make window ignore app visibility and instead depend purely on the decor * view visibility for determining window visibility. This is used by recents to keep diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 76914f3fd8ee0..3370db1fb8771 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -757,7 +757,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); - mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION + | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; mWindowManager.addView(mOrientationHandle, mOrientationParams); mOrientationHandle.setVisibility(View.GONE); mOrientationParams.setFitInsetsTypes(0 /* types*/); @@ -1563,7 +1564,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } lp.token = new Binder(); lp.accessibilityTitle = mContext.getString(R.string.nav_bar); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC + | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; lp.windowAnimations = 0; lp.setTitle("NavigationBar" + mContext.getDisplayId()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java index 338555e4c9fc6..7d2e9bf26b01c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java @@ -30,6 +30,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; import static org.junit.Assert.assertEquals; @@ -329,6 +330,23 @@ public class WindowLayoutTests { assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrame); } + @Test + public void layoutExtendedToDisplayCutout() { + addDisplayCutout(); + final int height = DISPLAY_HEIGHT / 2; + mRequestedHeight = UNSPECIFIED_LENGTH; + mAttrs.height = height; + mAttrs.gravity = Gravity.TOP; + mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mAttrs.setFitInsetsTypes(0); + mAttrs.privateFlags |= PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; + computeFrames(); + + assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayFrame); + assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mParentFrame); + assertRect(0, 0, DISPLAY_WIDTH, height + DISPLAY_CUTOUT_HEIGHT, mFrame); + } + @Test public void layoutInDisplayCutoutModeDefaultWithInvisibleSystemBars() { addDisplayCutout();