Move logic about computing window frames to the client side

This CL moves the logic about clipping window frames by display cutouts
to the client side. Without this, the very first window frame might be
incorrect, and that can make the framework dispatch the wrong insets to
the app.

This CL is also a step to layout windows at the client side, and
introduces a new class, WindowLayout, which will contain all the logic
about computing the window frame. For now, it only computes the
display frame and the parent frame, but eventually, it will compute the
window frame.

Bug: 197245443
Bug: 161810301
BUg: 175858823
Test: 1. Steps in b/197245443
      2. atest DisplayPolicyLayoutTests
Change-Id: Icafe76112547f4558c7397a1fc5fd08d87639a4d
This commit is contained in:
Tiger Huang
2021-09-23 20:40:55 +08:00
parent e758ed8089
commit bd6a43f1eb
5 changed files with 222 additions and 163 deletions

View File

@@ -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;
}

View File

@@ -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<Integer> 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);
}
/**

View File

@@ -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;
}
}

View File

@@ -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(

View File

@@ -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() {