diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 39172794f6028..61798817772b4 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -317,6 +317,26 @@ public class InsetsState implements Parcelable { return insets; } + public Insets calculateInsets(Rect frame, @InsetsType int types, + InsetsVisibilities overrideVisibilities) { + Insets insets = Insets.NONE; + for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { + InsetsSource source = mSources[type]; + if (source == null) { + continue; + } + int publicType = InsetsState.toPublicType(type); + if ((publicType & types) == 0) { + continue; + } + if (!overrideVisibilities.getVisibility(type)) { + continue; + } + insets = Insets.max(source.calculateInsets(frame, true), insets); + } + return insets; + } + public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) { Insets insets = Insets.NONE; for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { @@ -482,6 +502,26 @@ public class InsetsState implements Parcelable { return mDisplayCutout.get(); } + public void getDisplayCutoutSafe(Rect outBounds) { + outBounds.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + final DisplayCutout cutout = mDisplayCutout.get(); + final Rect displayFrame = mDisplayFrame; + if (!cutout.isEmpty()) { + if (cutout.getSafeInsetLeft() > 0) { + outBounds.left = displayFrame.left + cutout.getSafeInsetLeft(); + } + if (cutout.getSafeInsetTop() > 0) { + outBounds.top = displayFrame.top + cutout.getSafeInsetTop(); + } + if (cutout.getSafeInsetRight() > 0) { + outBounds.right = displayFrame.right - cutout.getSafeInsetRight(); + } + if (cutout.getSafeInsetBottom() > 0) { + outBounds.bottom = displayFrame.bottom - cutout.getSafeInsetBottom(); + } + } + } + public void setRoundedCorners(RoundedCorners roundedCorners) { mRoundedCorners = roundedCorners; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 15d5555afcc9d..1b2b182a555b1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -140,7 +140,6 @@ import android.os.Trace; import android.os.UserHandle; import android.sysprop.DisplayProperties; import android.util.AndroidRuntimeException; -import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.IndentingPrintWriter; @@ -160,7 +159,6 @@ import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; import android.view.Window.OnContentApplyWindowInsetsListener; -import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -510,9 +508,11 @@ public final class ViewRootImpl implements ViewParent, private final Point mSurfaceSize = new Point(); private final Point mLastSurfaceSize = new Point(); - final Rect mTempRect; // used in the transaction to not thrash the heap. - final Rect mVisRect; // used to retrieve visible rect of focused view. - private final Rect mTempBoundsRect = new Rect(); // used to set the size of the bounds surface. + private final Rect mVisRect = new Rect(); // used to retrieve visible rect of focused view. + private final Rect mTempRect = new Rect(); + private final Rect mTempRect2 = new Rect(); + + private final WindowLayout mWindowLayout = new WindowLayout(); // This is used to reduce the race between window focus changes being dispatched from // the window manager and input events coming through the input system. @@ -808,8 +808,6 @@ public final class ViewRootImpl implements ViewParent, mWidth = -1; mHeight = -1; mDirty = new Rect(); - mTempRect = new Rect(); - mVisRect = new Rect(); mWinFrame = new Rect(); mWindow = new W(this); mLeashToken = new Binder(); @@ -984,29 +982,6 @@ public final class ViewRootImpl implements ViewParent, } } - // TODO(b/161810301): Make this private after window layout is moved to the client side. - public static void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state, - Rect displayFrame, Rect outBounds) { - final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); - final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); - final ArraySet types = InsetsState.toInternalType(typesToFit); - final Rect df = displayFrame; - Insets insets = Insets.of(0, 0, 0, 0); - for (int i = types.size() - 1; i >= 0; i--) { - final InsetsSource source = state.peekSource(types.valueAt(i)); - if (source == null) { - continue; - } - insets = Insets.max(insets, source.calculateInsets( - df, attrs.isFitInsetsIgnoringVisibility())); - } - final int left = (sidesToFit & WindowInsets.Side.LEFT) != 0 ? insets.left : 0; - final int top = (sidesToFit & WindowInsets.Side.TOP) != 0 ? insets.top : 0; - final int right = (sidesToFit & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0; - final int bottom = (sidesToFit & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0; - outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom); - } - private Configuration getConfiguration() { return mContext.getResources().getConfiguration(); } @@ -1167,8 +1142,13 @@ public final class ViewRootImpl implements ViewParent, mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars; mInsetsController.onStateChanged(mTempInsets); mInsetsController.onControlsChanged(mTempControls); - computeWindowBounds(mWindowAttributes, mInsetsController.getState(), - getConfiguration().windowConfiguration.getBounds(), mTmpFrames.frame); + final InsetsState state = mInsetsController.getState(); + final Rect displayCutoutSafe = mTempRect; + state.getDisplayCutoutSafe(displayCutoutSafe); + mWindowLayout.computeWindowFrames(mWindowAttributes, state, + displayCutoutSafe, getConfiguration().windowConfiguration.getBounds(), + mInsetsController.getRequestedVisibilities(), + null /* attachedWindowFrame */, mTmpFrames.frame, mTempRect2); setFrame(mTmpFrames.frame); if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { @@ -1965,11 +1945,11 @@ public final class ViewRootImpl implements ViewParent, private void setBoundsLayerCrop(Transaction t) { // Adjust of insets and update the bounds layer so child surfaces do not draw into // the surface inset region. - mTempBoundsRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y); - mTempBoundsRect.inset(mWindowAttributes.surfaceInsets.left, + mTempRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y); + mTempRect.inset(mWindowAttributes.surfaceInsets.left, mWindowAttributes.surfaceInsets.top, mWindowAttributes.surfaceInsets.right, mWindowAttributes.surfaceInsets.bottom); - t.setWindowCrop(mBoundsLayer, mTempBoundsRect); + t.setWindowCrop(mBoundsLayer, mTempRect); } /** diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java new file mode 100644 index 0000000000000..cdc1977eaad69 --- /dev/null +++ b/core/java/android/view/WindowLayout.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.InsetsState.ITYPE_IME; +import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; +import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; +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.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; + +import android.graphics.Insets; +import android.graphics.Rect; + +/** + * Computes window frames. + * @hide + */ +public class WindowLayout { + private final Rect mTempDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); + private final Rect mTempRect = new Rect(); + + public boolean computeWindowFrames(WindowManager.LayoutParams attrs, InsetsState state, + Rect displayCutoutSafe, Rect windowBounds, InsetsVisibilities requestedVisibilities, + Rect attachedWindowFrame, Rect outDisplayFrame, Rect outParentFrame) { + final int type = attrs.type; + final int fl = attrs.flags; + final int pfl = attrs.privateFlags; + final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; + + // Compute bounds restricted by insets + final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(), + attrs.isFitInsetsIgnoringVisibility()); + final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides(); + final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0; + final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0; + final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0; + final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0; + outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top, + windowBounds.right - right, windowBounds.bottom - bottom); + + if (attachedWindowFrame == null) { + outParentFrame.set(outDisplayFrame); + if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { + final InsetsSource source = state.peekSource(ITYPE_IME); + if (source != null) { + outParentFrame.inset(source.calculateInsets( + outParentFrame, false /* ignoreVisibility */)); + } + } + } else { + outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame); + } + + // Compute bounds restricted by display cutout + final DisplayCutout cutout = state.getDisplayCutout(); + if (cutout.isEmpty()) { + return false; + } + boolean clippedByDisplayCutout = false; + final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect; + displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe); + + // Ensure that windows with a non-ALWAYS display cutout mode are laid out in + // the cutout safe zone. + final int cutoutMode = attrs.layoutInDisplayCutoutMode; + if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { + final Rect displayFrame = state.getDisplayFrame(); + final InsetsSource statusBarSource = state.peekSource(ITYPE_STATUS_BAR); + if (statusBarSource != null && displayCutoutSafe.top > displayFrame.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayCutoutSafeExceptMaybeBars.top = + Math.max(statusBarSource.getFrame().bottom, displayCutoutSafe.top); + } + if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { + if (displayFrame.width() < displayFrame.height()) { + displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + } else { + displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; + displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; + } + } + final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0; + if (layoutInScreen && layoutInsetDecor + && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT + || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { + final Insets systemBarsInsets = state.calculateInsets( + displayFrame, WindowInsets.Type.systemBars(), requestedVisibilities); + if (systemBarsInsets.left > 0) { + displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; + } + if (systemBarsInsets.top > 0) { + displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; + } + if (systemBarsInsets.right > 0) { + displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; + } + if (systemBarsInsets.bottom > 0) { + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + } + } + if (type == TYPE_INPUT_METHOD) { + final InsetsSource navSource = state.peekSource(ITYPE_NAVIGATION_BAR); + if (navSource != null && navSource.calculateInsets(displayFrame, true).bottom > 0) { + // The IME can always extend under the bottom cutout if the navbar is there. + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + } + } + final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen; + + // TYPE_BASE_APPLICATION windows are never considered floating here because they don't + // get cropped / shifted to the displayFrame in WindowState. + final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen + && type != TYPE_BASE_APPLICATION; + + // Windows that are attached to a parent and laid out in said parent already avoid + // the cutout according to that parent and don't need to be further constrained. + // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. + // They will later be cropped or shifted using the displayFrame in WindowState, + // which prevents overlap with the DisplayCutout. + if (!attachedInParent && !floatingInScreenWindow) { + mTempRect.set(outParentFrame); + outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars); + clippedByDisplayCutout = !mTempRect.equals(outParentFrame); + } + outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars); + } + return clippedByDisplayCutout; + } +} diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java index 32e43ca4e56c1..45d7141fd5563 100644 --- a/services/core/java/com/android/server/wm/DisplayFrames.java +++ b/services/core/java/com/android/server/wm/DisplayFrames.java @@ -84,7 +84,7 @@ public class DisplayFrames { final Rect safe = mDisplayCutoutSafe; final DisplayCutout cutout = displayCutout.getDisplayCutout(); if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight - && state.getDisplayCutout().equals(cutout) + && state.getDisplayCutout().equals(cutout) && state.getRoundedCorners().equals(roundedCorners) && state.getPrivacyIndicatorBounds().equals(indicatorBounds)) { return false; @@ -93,24 +93,12 @@ public class DisplayFrames { mDisplayHeight = info.logicalHeight; final Rect unrestricted = mUnrestricted; unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight); - safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); state.setDisplayFrame(unrestricted); state.setDisplayCutout(cutout); state.setRoundedCorners(roundedCorners); state.setPrivacyIndicatorBounds(indicatorBounds); + state.getDisplayCutoutSafe(safe); if (!cutout.isEmpty()) { - if (cutout.getSafeInsetLeft() > 0) { - safe.left = unrestricted.left + cutout.getSafeInsetLeft(); - } - if (cutout.getSafeInsetTop() > 0) { - safe.top = unrestricted.top + cutout.getSafeInsetTop(); - } - if (cutout.getSafeInsetRight() > 0) { - safe.right = unrestricted.right - cutout.getSafeInsetRight(); - } - if (cutout.getSafeInsetBottom() > 0) { - safe.bottom = unrestricted.bottom - cutout.getSafeInsetBottom(); - } state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame( unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom); state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame( diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 881bd353ad469..6aacb7535c8c4 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,7 +29,6 @@ import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_CLIMATE_BAR; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; -import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; @@ -37,7 +36,6 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; -import static android.view.ViewRootImpl.computeWindowBounds; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; @@ -50,21 +48,15 @@ import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; 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_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; @@ -147,6 +139,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; +import android.view.WindowLayout; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; @@ -356,15 +349,15 @@ public class DisplayPolicy { // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. private long mPendingPanicGestureUptime; - private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); private static final Rect sTmpRect = new Rect(); private static final Rect sTmpNavFrame = new Rect(); private static final Rect sTmpStatusFrame = new Rect(); private static final Rect sTmpDecorFrame = new Rect(); - private static final Rect sTmpScreenDecorFrame = new Rect(); private static final Rect sTmpLastParentFrame = new Rect(); private static final Rect sTmpDisplayFrameBounds = new Rect(); + private final WindowLayout mWindowLayout = new WindowLayout(); + private WindowState mTopFullscreenOpaqueWindowState; private boolean mTopIsFullscreen; private boolean mForceStatusBar; @@ -1492,7 +1485,7 @@ public class DisplayPolicy { * @param outInsetsState The insets state of this display from the client's perspective. * @param localClient Whether the client is from the our process. * @return Whether to always consume the system bars. - * See {@link #areSystemBarsForcedShownLw(WindowState)}. + * See {@link #areSystemBarsForcedShownLw()}. */ boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState, boolean localClient) { @@ -1709,108 +1702,24 @@ public class DisplayPolicy { final int type = attrs.type; final int fl = attrs.flags; - final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; displayFrames = win.getDisplayFrames(displayFrames); final WindowFrames windowFrames = win.getLayoutingWindowFrames(); - sTmpLastParentFrame.set(windowFrames.mParentFrame); final Rect pf = windowFrames.mParentFrame; final Rect df = windowFrames.mDisplayFrame; - windowFrames.setParentFrameWasClippedByDisplayCutout(false); + final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null; + sTmpLastParentFrame.set(pf); - final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; - final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; + // Override the bounds in window token has many side effects. Directly use the display + // frame set for the simulated layout for this case. + final Rect winBounds = windowFrames.mIsSimulatingDecorWindow ? df : win.getBounds(); - final InsetsState state = win.getInsetsState(); - if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) { - // Override the bounds in window token has many side effects. Directly use the display - // frame set for the simulated layout for this case. - computeWindowBounds(attrs, state, df, df); - } else { - computeWindowBounds(attrs, state, win.getBounds(), df); - } - if (attached == null) { - pf.set(df); - if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { - final InsetsSource source = state.peekSource(ITYPE_IME); - if (source != null) { - pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); - } - } - } else { - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); - } - - final int cutoutMode = attrs.layoutInDisplayCutoutMode; - // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in - // the cutout safe zone. - if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { - final boolean attachedInParent = attached != null && !layoutInScreen; - final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR); - final boolean requestedHideNavigation = - !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); - - // TYPE_BASE_APPLICATION windows are never considered floating here because they don't - // get cropped / shifted to the displayFrame in WindowState. - final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen - && type != TYPE_BASE_APPLICATION; - final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; - displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); - if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { - if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) { - displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; - displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; - } else { - displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; - displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; - } - } - - if (layoutInScreen && layoutInsetDecor && !requestedFullscreen - && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { - // At the top we have the status bar, so apps that are - // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN - // already expect that there's an inset there and we don't need to exclude - // the window from that area. - displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; - } - if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation - && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { - // Same for the navigation bar. - switch (mNavigationBarPosition) { - case NAV_BAR_BOTTOM: - displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; - break; - case NAV_BAR_RIGHT: - displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; - break; - case NAV_BAR_LEFT: - displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; - break; - } - } - if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { - // The IME can always extend under the bottom cutout if the navbar is there. - displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; - } - // Windows that are attached to a parent and laid out in said parent already avoid - // the cutout according to that parent and don't need to be further constrained. - // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. - // They will later be cropped or shifted using the displayFrame in WindowState, - // which prevents overlap with the DisplayCutout. - if (!attachedInParent && !floatingInScreenWindow) { - sTmpRect.set(pf); - pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); - windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); - } - // Make sure that NO_LIMITS windows clipped to the display don't extend under the - // cutout. - df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); - } + final boolean clippedByDisplayCutout = mWindowLayout.computeWindowFrames(attrs, + win.getInsetsState(), displayFrames.mDisplayCutoutSafe, winBounds, + win.getRequestedVisibilities(), attachedWindowFrame, df, pf); + windowFrames.setParentFrameWasClippedByDisplayCutout(clippedByDisplayCutout); // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. // Also, we don't allow windows in multi-window mode to extend out of the screen. @@ -1831,16 +1740,6 @@ public class DisplayPolicy { } win.computeFrameAndUpdateSourceFrame(displayFrames); - if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) { - if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { - // Make sure that the zone we're avoiding for the cutout is at least as tall as the - // status bar; otherwise fullscreen apps will end up cutting halfway into the status - // bar. - displayFrames.mDisplayCutoutSafe.top = Math.max( - displayFrames.mDisplayCutoutSafe.top, - windowFrames.mFrame.bottom); - } - } } WindowState getTopFullscreenOpaqueWindow() {