| /* |
| * Copyright (C) 2018 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 com.android.server.wm; |
| |
| import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME; |
| import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME; |
| import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS; |
| import static com.android.server.wm.WindowFramesProto.CUTOUT; |
| import static com.android.server.wm.WindowFramesProto.DECOR_FRAME; |
| import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME; |
| import static com.android.server.wm.WindowFramesProto.FRAME; |
| import static com.android.server.wm.WindowFramesProto.OUTSETS; |
| import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME; |
| import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME; |
| import static com.android.server.wm.WindowFramesProto.OVERSCAN_INSETS; |
| import static com.android.server.wm.WindowFramesProto.PARENT_FRAME; |
| import static com.android.server.wm.WindowFramesProto.STABLE_INSETS; |
| import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME; |
| import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS; |
| |
| import android.annotation.NonNull; |
| import android.graphics.Rect; |
| import android.util.proto.ProtoOutputStream; |
| import android.view.DisplayCutout; |
| |
| import com.android.server.wm.utils.InsetUtils; |
| import com.android.server.wm.utils.WmDisplayCutout; |
| |
| import java.io.PrintWriter; |
| |
| /** |
| * Container class for all the window frames that affect how windows are laid out. |
| * |
| * TODO(b/111611553): Investigate which frames are still needed and which are duplicates |
| */ |
| public class WindowFrames { |
| private static final StringBuilder sTmpSB = new StringBuilder(); |
| |
| /** |
| * In most cases, this is the area of the entire screen. |
| * |
| * TODO(b/111611553): The name is unclear and most likely should be swapped with |
| * {@link #mDisplayFrame} |
| * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine |
| * whether this is still necessary to do. |
| */ |
| public final Rect mParentFrame = new Rect(); |
| |
| /** |
| * The entire screen area of the {@link TaskStack} this window is in. Usually equal to the |
| * screen area of the device. |
| * |
| * TODO(b/111611553): The name is unclear and most likely should be swapped with |
| * {@link #mParentFrame} |
| */ |
| public final Rect mDisplayFrame = new Rect(); |
| |
| /** |
| * The region of the display frame that the display type supports displaying content on. This |
| * is mostly a special case for TV where some displays don’t have the entire display usable. |
| * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to |
| * allow window display contents to extend into the overscan region. |
| */ |
| public final Rect mOverscanFrame = new Rect(); |
| |
| /** |
| * Legacy stuff. Generally equal to the content frame expect when the IME for older apps |
| * displays hint text. |
| */ |
| public final Rect mVisibleFrame = new Rect(); |
| |
| /** |
| * The area not occupied by the status and navigation bars. So, if both status and navigation |
| * bars are visible, the decor frame is equal to the stable frame. |
| */ |
| public final Rect mDecorFrame = new Rect(); |
| |
| /** |
| * Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame |
| * minus the area occupied by the IME if the IME is present. |
| */ |
| public final Rect mContentFrame = new Rect(); |
| |
| /** |
| * The display frame minus the stable insets. This value is always constant regardless of if |
| * the status bar or navigation bar is visible. |
| */ |
| public final Rect mStableFrame = new Rect(); |
| |
| /** |
| * Frame that includes dead area outside of the surface but where we want to pretend that it's |
| * possible to draw. |
| */ |
| final public Rect mOutsetFrame = new Rect(); |
| |
| /** |
| * Similar to {@link #mDisplayFrame} |
| * |
| * TODO: Why is this different than mDisplayFrame |
| */ |
| final Rect mContainingFrame = new Rect(); |
| |
| /** |
| * "Real" frame that the application sees, in display coordinate space. |
| */ |
| final Rect mFrame = new Rect(); |
| |
| /** |
| * The last real frame that was reported to the client. |
| */ |
| final Rect mLastFrame = new Rect(); |
| |
| private boolean mFrameSizeChanged = false; |
| |
| // Frame that is scaled to the application's coordinate space when in |
| // screen size compatibility mode. |
| final Rect mCompatFrame = new Rect(); |
| |
| /** |
| * Whether the parent frame would have been different if there was no display cutout. |
| */ |
| private boolean mParentFrameWasClippedByDisplayCutout; |
| |
| /** |
| * Part of the display that has been cut away. See {@link DisplayCutout}. |
| */ |
| WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT; |
| |
| /** |
| * The last cutout that has been reported to the client. |
| */ |
| private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT; |
| |
| private boolean mDisplayCutoutChanged; |
| |
| /** |
| * Insets that determine the area covered by the display overscan region. These are in the |
| * application's coordinate space (without compatibility scale applied). |
| */ |
| final Rect mOverscanInsets = new Rect(); |
| final Rect mLastOverscanInsets = new Rect(); |
| private boolean mOverscanInsetsChanged; |
| |
| /** |
| * Insets that determine the area covered by the stable system windows. These are in the |
| * application's coordinate space (without compatibility scale applied). |
| */ |
| final Rect mStableInsets = new Rect(); |
| final Rect mLastStableInsets = new Rect(); |
| private boolean mStableInsetsChanged; |
| |
| /** |
| * Outsets determine the area outside of the surface where we want to pretend that it's possible |
| * to draw anyway. |
| */ |
| final Rect mOutsets = new Rect(); |
| final Rect mLastOutsets = new Rect(); |
| private boolean mOutsetsChanged = false; |
| |
| /** |
| * Insets that determine the actually visible area. These are in the application's |
| * coordinate space (without compatibility scale applied). |
| */ |
| final Rect mVisibleInsets = new Rect(); |
| final Rect mLastVisibleInsets = new Rect(); |
| private boolean mVisibleInsetsChanged; |
| |
| /** |
| * Insets that are covered by system windows (such as the status bar) and |
| * transient docking windows (such as the IME). These are in the application's |
| * coordinate space (without compatibility scale applied). |
| */ |
| final Rect mContentInsets = new Rect(); |
| final Rect mLastContentInsets = new Rect(); |
| private boolean mContentInsetsChanged; |
| |
| private final Rect mTmpRect = new Rect(); |
| |
| private boolean mHasOutsets; |
| |
| private boolean mContentChanged; |
| |
| public WindowFrames() { |
| } |
| |
| public WindowFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame, Rect contentFrame, |
| Rect visibleFrame, Rect decorFrame, Rect stableFrame, Rect outsetFrame) { |
| setFrames(parentFrame, displayFrame, overscanFrame, contentFrame, visibleFrame, decorFrame, |
| stableFrame, outsetFrame); |
| } |
| |
| public void setFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame, |
| Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame, |
| Rect outsetFrame) { |
| mParentFrame.set(parentFrame); |
| mDisplayFrame.set(displayFrame); |
| mOverscanFrame.set(overscanFrame); |
| mContentFrame.set(contentFrame); |
| mVisibleFrame.set(visibleFrame); |
| mDecorFrame.set(decorFrame); |
| mStableFrame.set(stableFrame); |
| mOutsetFrame.set(outsetFrame); |
| } |
| |
| public void setParentFrameWasClippedByDisplayCutout( |
| boolean parentFrameWasClippedByDisplayCutout) { |
| mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout; |
| } |
| |
| boolean parentFrameWasClippedByDisplayCutout() { |
| return mParentFrameWasClippedByDisplayCutout; |
| } |
| |
| public void setDisplayCutout(WmDisplayCutout displayCutout) { |
| mDisplayCutout = displayCutout; |
| } |
| |
| /** |
| * @return true if the width or height has changed since last reported to the client. |
| */ |
| private boolean didFrameSizeChange() { |
| return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); |
| } |
| |
| /** |
| * Calculates the outsets for this windowFrame. The outsets are calculated by the area between |
| * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then |
| * {@link #mOutsets} is set to empty. |
| */ |
| void calculateOutsets() { |
| if (mHasOutsets) { |
| InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets); |
| } |
| } |
| |
| /** |
| * Calculate the insets for the type |
| * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER} |
| * |
| * @param cutoutInsets The insets for the cutout. |
| */ |
| void calculateDockedDividerInsets(Rect cutoutInsets) { |
| // For the docked divider, we calculate the stable insets like a full-screen window |
| // so it can use it to calculate the snap positions. |
| mTmpRect.set(mDisplayFrame); |
| mTmpRect.inset(cutoutInsets); |
| mTmpRect.intersectUnchecked(mStableFrame); |
| InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets); |
| |
| // The divider doesn't care about insets in any case, so set it to empty so we don't |
| // trigger a relayout when moving it. |
| mContentInsets.setEmpty(); |
| mVisibleInsets.setEmpty(); |
| mDisplayCutout = WmDisplayCutout.NO_CUTOUT; |
| } |
| |
| /** |
| * Calculate the insets for a window. |
| * |
| * @param windowsAreFloating Whether the window is in a floating task such as pinned or |
| * freeform |
| * @param inFullscreenContainer Whether the window is in a container that takes up the screen's |
| * entire space |
| * @param windowBounds The bounds for the window |
| */ |
| void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer, |
| Rect windowBounds) { |
| // Override right and/or bottom insets in case if the frame doesn't fit the screen in |
| // non-fullscreen mode. |
| boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer |
| && mFrame.right > windowBounds.right; |
| boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer |
| && mFrame.bottom > windowBounds.bottom; |
| |
| mTmpRect.set(mFrame.left, mFrame.top, |
| overrideRightInset ? windowBounds.right : mFrame.right, |
| overrideBottomInset ? windowBounds.bottom : mFrame.bottom); |
| |
| InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets); |
| InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets); |
| InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets); |
| } |
| |
| /** |
| * Scales all the insets by a specific amount. |
| * |
| * @param scale The amount to scale the insets by. |
| */ |
| void scaleInsets(float scale) { |
| mOverscanInsets.scale(scale); |
| mContentInsets.scale(scale); |
| mVisibleInsets.scale(scale); |
| mStableInsets.scale(scale); |
| mOutsets.scale(scale); |
| } |
| |
| void offsetFrames(int layoutXDiff, int layoutYDiff) { |
| mFrame.offset(layoutXDiff, layoutYDiff); |
| mContentFrame.offset(layoutXDiff, layoutYDiff); |
| mVisibleFrame.offset(layoutXDiff, layoutYDiff); |
| mStableFrame.offset(layoutXDiff, layoutYDiff); |
| } |
| |
| /** |
| * Updates info about whether the size of the window has changed since last reported. |
| * |
| * @return true if info about size has changed since last reported. |
| */ |
| boolean setReportResizeHints() { |
| mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets); |
| mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets); |
| mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets); |
| mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets); |
| mOutsetsChanged |= !mLastOutsets.equals(mOutsets); |
| mFrameSizeChanged |= didFrameSizeChange(); |
| mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout); |
| return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged |
| || mStableInsetsChanged || mOutsetsChanged || mFrameSizeChanged |
| || mDisplayCutoutChanged; |
| } |
| |
| /** |
| * Resets the insets changed flags so they're all set to false again. This should be called |
| * after the insets are reported to client. |
| */ |
| void resetInsetsChanged() { |
| mOverscanInsetsChanged = false; |
| mContentInsetsChanged = false; |
| mVisibleInsetsChanged = false; |
| mStableInsetsChanged = false; |
| mOutsetsChanged = false; |
| mFrameSizeChanged = false; |
| mDisplayCutoutChanged = false; |
| } |
| |
| /** |
| * Copy over inset values as the last insets that were sent to the client. |
| */ |
| void updateLastInsetValues() { |
| mLastOverscanInsets.set(mOverscanInsets); |
| mLastContentInsets.set(mContentInsets); |
| mLastVisibleInsets.set(mVisibleInsets); |
| mLastStableInsets.set(mStableInsets); |
| mLastOutsets.set(mOutsets); |
| mLastDisplayCutout = mDisplayCutout; |
| } |
| |
| /** |
| * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update |
| * the client. |
| */ |
| void resetLastContentInsets() { |
| mLastContentInsets.set(-1, -1, -1, -1); |
| } |
| |
| /** |
| * Sets whether the frame has outsets. |
| */ |
| public void setHasOutsets(boolean hasOutsets) { |
| if (mHasOutsets == hasOutsets) { |
| return; |
| } |
| mHasOutsets = hasOutsets; |
| if (!hasOutsets) { |
| mOutsets.setEmpty(); |
| } |
| } |
| |
| /** |
| * Sets whether the content has changed. This means that either the size or parent frame has |
| * changed. |
| */ |
| public void setContentChanged(boolean contentChanged) { |
| mContentChanged = contentChanged; |
| } |
| |
| /** |
| * @see #setContentChanged(boolean) |
| */ |
| boolean hasContentChanged() { |
| return mContentChanged; |
| } |
| |
| public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) { |
| final long token = proto.start(fieldId); |
| mParentFrame.writeToProto(proto, PARENT_FRAME); |
| mContentFrame.writeToProto(proto, CONTENT_FRAME); |
| mDisplayFrame.writeToProto(proto, DISPLAY_FRAME); |
| mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME); |
| mVisibleFrame.writeToProto(proto, VISIBLE_FRAME); |
| mDecorFrame.writeToProto(proto, DECOR_FRAME); |
| mOutsetFrame.writeToProto(proto, OUTSET_FRAME); |
| mContainingFrame.writeToProto(proto, CONTAINING_FRAME); |
| mFrame.writeToProto(proto, FRAME); |
| mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT); |
| mContentInsets.writeToProto(proto, CONTENT_INSETS); |
| mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS); |
| mVisibleInsets.writeToProto(proto, VISIBLE_INSETS); |
| mStableInsets.writeToProto(proto, STABLE_INSETS); |
| mOutsets.writeToProto(proto, OUTSETS); |
| |
| proto.end(token); |
| } |
| |
| public void dump(PrintWriter pw, String prefix) { |
| pw.println(prefix + "Frames: containing=" |
| + mContainingFrame.toShortString(sTmpSB) |
| + " parent=" + mParentFrame.toShortString(sTmpSB)); |
| pw.println(prefix + " display=" + mDisplayFrame.toShortString(sTmpSB) |
| + " overscan=" + mOverscanFrame.toShortString(sTmpSB)); |
| pw.println(prefix + " content=" + mContentFrame.toShortString(sTmpSB) |
| + " visible=" + mVisibleFrame.toShortString(sTmpSB)); |
| pw.println(prefix + " decor=" + mDecorFrame.toShortString(sTmpSB)); |
| pw.println(prefix + " outset=" + mOutsetFrame.toShortString(sTmpSB)); |
| pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB) |
| + " last=" + mLastFrame.toShortString(sTmpSB)); |
| pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout() |
| + " last=" + mLastDisplayCutout.getDisplayCutout()); |
| pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB) |
| + " content=" + mContentInsets.toShortString(sTmpSB) |
| + " visible=" + mVisibleInsets.toShortString(sTmpSB) |
| + " stable=" + mStableInsets.toShortString(sTmpSB) |
| + " outsets=" + mOutsets.toShortString(sTmpSB)); |
| pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB) |
| + " content=" + mLastContentInsets.toShortString(sTmpSB) |
| + " visible=" + mLastVisibleInsets.toShortString(sTmpSB) |
| + " stable=" + mLastStableInsets.toShortString(sTmpSB) |
| + " outset=" + mLastOutsets.toShortString(sTmpSB)); |
| } |
| |
| String getInsetsInfo() { |
| return "ci=" + mContentInsets.toShortString() |
| + " vi=" + mVisibleInsets.toShortString() |
| + " si=" + mStableInsets.toShortString() |
| + " of=" + mOutsets.toShortString(); |
| } |
| |
| String getInsetsChangedInfo() { |
| return "contentInsetsChanged=" + mContentInsetsChanged |
| + " " + mContentInsets.toShortString() |
| + " visibleInsetsChanged=" + mVisibleInsetsChanged |
| + " " + mVisibleInsets.toShortString() |
| + " stableInsetsChanged=" + mStableInsetsChanged |
| + " " + mStableInsets.toShortString() |
| + " outsetsChanged=" + mOutsetsChanged |
| + " " + mOutsets.toShortString() |
| + " displayCutoutChanged=" + mDisplayCutoutChanged; |
| } |
| } |