Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a5dffa77 authored by Yunfan Chen's avatar Yunfan Chen
Browse files

Introduce a private flag to extend frame with cutout

This patch introduced a new private flag
PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_TO_CUTOUT to let the window extend
it's requested window frame to contain the display cutout frame. This
is useful for the case of navigation bar. Without it, the navigation
bar may not have enough space to layout it's content when it's on the
same side as the display cutout. With the flag set, the Navigation
bar will have the requested size plus the navigation bar size as its
frame.

Bug: 203031262
Bug: 161689946
Test: See reproduce steps in b/203031262
Test: atest WindowLayoutTests
Change-Id: Id1d6f09dc421b5f25e16e4aca6282b6deec2e8c4
parent 4b1a9e35
Loading
Loading
Loading
Loading
+31 −28
Original line number Original line Diff line number Diff line
@@ -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_FIT_INSETS_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
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_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_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
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
            if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
                    + ", desiredWindowWidth=" + desiredWindowWidth);
                    + ", desiredWindowWidth=" + desiredWindowWidth);
            if (baseSize != 0 && desiredWindowWidth > baseSize) {
            if (baseSize != 0 && desiredWindowWidth > baseSize) {
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                        lp.privateFlags);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight()
                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight()
@@ -2475,7 +2477,7 @@ public final class ViewRootImpl implements ViewParent,
                    baseSize = (baseSize+desiredWindowWidth)/2;
                    baseSize = (baseSize+desiredWindowWidth)/2;
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                            + baseSize);
                            + baseSize);
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width, lp.privateFlags);
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
@@ -2488,8 +2490,10 @@ public final class ViewRootImpl implements ViewParent,
        }
        }


        if (!goodMeasure) {
        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                    lp.privateFlags);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                    lp.privateFlags);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                windowSizeMayChange = true;
                windowSizeMayChange = true;
@@ -3149,8 +3153,10 @@ public final class ViewRootImpl implements ViewParent,
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
                        || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
                        updatedConfiguration) {
                        updatedConfiguration) {
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
                            lp.privateFlags);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,
                            lp.privateFlags);


                    if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                    if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                            + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                            + mWidth + " measuredWidth=" + host.getMeasuredWidth()
@@ -3950,19 +3956,16 @@ public final class ViewRootImpl implements ViewParent,
     * Figures out the measure spec for the root view in a window based on it's
     * Figures out the measure spec for the root view in a window based on it's
     * layout params.
     * layout params.
     *
     *
     * @param windowSize
     * @param windowSize The available width or height of the window.
     *            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.
     * @param rootDimension
     *            The layout params for one dimension (width or height) of the
     *            window.
     *
     * @return The measure spec to use to measure the root view.
     * @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;
        int measureSpec;
        final int rootDimension = (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0
                ? MATCH_PARENT : measurement;
        switch (rootDimension) {
        switch (rootDimension) {

            case ViewGroup.LayoutParams.MATCH_PARENT:
            case ViewGroup.LayoutParams.MATCH_PARENT:
                // Window can't resize. Force root view to be windowSize.
                // Window can't resize. Force root view to be windowSize.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
+31 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package android.view;
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_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_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.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_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_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_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
@@ -250,11 +253,39 @@ public class WindowLayout {
        Gravity.apply(attrs.gravity, w, h, outParentFrame,
        Gravity.apply(attrs.gravity, w, h, outParentFrame,
                (int) (x + attrs.horizontalMargin * pw),
                (int) (x + attrs.horizontalMargin * pw),
                (int) (y + attrs.verticalMargin * ph), outFrame);
                (int) (y + attrs.verticalMargin * ph), outFrame);

        // Now make sure the window fits in the overall display frame.
        // Now make sure the window fits in the overall display frame.
        if (fitToDisplay) {
        if (fitToDisplay) {
            Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
            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()
        if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle()
                + " outFrame=" + outFrame.toShortString()
                + " outFrame=" + outFrame.toShortString()
                + " outParentFrame=" + outParentFrame.toShortString()
                + " outParentFrame=" + outParentFrame.toShortString()
+10 −0
Original line number Original line Diff line number Diff line
@@ -2384,6 +2384,16 @@ public interface WindowManager extends ViewManager {
         */
         */
        public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 0x00001000;
        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
         * 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
         * view visibility for determining window visibility. This is used by recents to keep
+4 −2
Original line number Original line Diff line number Diff line
@@ -757,7 +757,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                PixelFormat.TRANSLUCENT);
                PixelFormat.TRANSLUCENT);
        mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
        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);
        mWindowManager.addView(mOrientationHandle, mOrientationParams);
        mOrientationHandle.setVisibility(View.GONE);
        mOrientationHandle.setVisibility(View.GONE);
        mOrientationParams.setFitInsetsTypes(0 /* types*/);
        mOrientationParams.setFitInsetsTypes(0 /* types*/);
@@ -1563,7 +1564,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
        }
        }
        lp.token = new Binder();
        lp.token = new Binder();
        lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
        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.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        lp.windowAnimations = 0;
        lp.windowAnimations = 0;
        lp.setTitle("NavigationBar" + mContext.getDisplayId());
        lp.setTitle("NavigationBar" + mContext.getDisplayId());
+18 −0
Original line number Original line Diff line number Diff line
@@ -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_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
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_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;


@@ -329,6 +330,23 @@ public class WindowLayoutTests {
        assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrame);
        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
    @Test
    public void layoutInDisplayCutoutModeDefaultWithInvisibleSystemBars() {
    public void layoutInDisplayCutoutModeDefaultWithInvisibleSystemBars() {
        addDisplayCutout();
        addDisplayCutout();