summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTiger Huang <tigerhuang@google.com>2021-09-23 20:40:55 +0800
committerTiger Huang <tigerhuang@google.com>2021-10-19 17:48:07 +0000
commitbd6a43f1ebee7768a5f1dbbf62b37aa1d6e83e23 (patch)
treec8661b9746878fb71d450b536c9cda0931e41fbf /core/java
parente758ed80891091af4c0d93b28c33ce243e2ecc0f (diff)
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
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/view/InsetsState.java40
-rw-r--r--core/java/android/view/ViewRootImpl.java50
-rw-r--r--core/java/android/view/WindowLayout.java152
3 files changed, 207 insertions, 35 deletions
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 39172794f602..61798817772b 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 15d5555afcc9..1b2b182a555b 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<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);
}
/**
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
new file mode 100644
index 000000000000..cdc1977eaad6
--- /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;
+ }
+}