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

Commit fa02da62 authored by Adrian Roos's avatar Adrian Roos
Browse files

DisplayCutout: LayoutInDisplayCutoutMode API

Replace the FLAG2_LAYOUT_IN_DISPLAY_CUTOUT flag with a
dedicated layoutInDisplayCutout field; given the change
in behavior of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN with respect
to the display cutout, apps that request this now also need
a way to request the same behavior as FLAG_FULLSCREEN.

Broadly, there's three categories of apps:

1) Apps that want to make dedicated use of the cutout
   area -> no letterbox ever

2a) Apps that hide the status bar, but don't expect the
    cutout to be there cutting into their content
    -> we want those to get letterboxed

 b) Some apps may only be transiently fullscreen, but always
    want to get letterboxed
     -> we want those to get letterboxed even if not currently
        fullscreen

3) Apps that never go fullscreen, and just draw the status
   bar background in the cutout area (i.e. the most common type
   of app)
   -> these need to get letterboxed whenever the cutout and
      status bar don't coincide (under our current guidelines
      that's only in fullscreen and landscape)

To cover each use case, we have:

ALWAYS: Always allow the app to draw into the cutout, never letterbox it; covers 1
NEVER: Never allow the app to draw into the cutout, always letterbox it; covers 2a and 2b
DEFAULT: Allow the app to draw into the cutout if that area is covered by the status bar
         anyways. This does the right thing for most existing apps (2a and 3).

Bug: 65689439
Test: atest PhoneWindowManagerLayoutTest
Change-Id: Ib8d551251e9be4ef9d580ca2151bf40a9678acae
parent 2dfb785e
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -47748,7 +47748,6 @@ package android.view {
    field public static final int FIRST_APPLICATION_WINDOW = 1; // 0x1
    field public static final int FIRST_SUB_WINDOW = 1000; // 0x3e8
    field public static final int FIRST_SYSTEM_WINDOW = 2000; // 0x7d0
    field public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 1L; // 0x1L
    field public static final int FLAGS_CHANGED = 4; // 0x4
    field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
    field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
@@ -47786,6 +47785,9 @@ package android.view {
    field public static final int LAST_SUB_WINDOW = 1999; // 0x7cf
    field public static final int LAST_SYSTEM_WINDOW = 2999; // 0xbb7
    field public static final int LAYOUT_CHANGED = 1; // 0x1
    field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1; // 0x1
    field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; // 0x0
    field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2; // 0x2
    field public static final int MEMORY_TYPE_CHANGED = 256; // 0x100
    field public static final deprecated int MEMORY_TYPE_GPU = 2; // 0x2
    field public static final deprecated int MEMORY_TYPE_HARDWARE = 1; // 0x1
@@ -47848,6 +47850,7 @@ package android.view {
    field public int gravity;
    field public float horizontalMargin;
    field public float horizontalWeight;
    field public int layoutInDisplayCutoutMode;
    field public deprecated int memoryType;
    field public java.lang.String packageName;
    field public int preferredDisplayModeId;
+12 −0
Original line number Diff line number Diff line
@@ -3387,6 +3387,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * decorations when they are shown.  You can perform layout of your inner
     * UI elements to account for non-fullscreen system UI through the
     * {@link #fitSystemWindows(Rect)} method.
     *
     * <p>Note: on displays that have a {@link DisplayCutout}, the window may still be placed
     *  differently than if {@link #SYSTEM_UI_FLAG_FULLSCREEN} was set, if the
     *  window's {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode
     *  layoutInDisplayCutoutMode} is
     *  {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
     *  LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}. To avoid this, use either of the other modes.
     *
     * @see WindowManager.LayoutParams#layoutInDisplayCutoutMode
     * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
     * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
     * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
     */
    public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
+4 −4
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
@@ -1597,9 +1597,9 @@ public final class ViewRootImpl implements ViewParent,

    void dispatchApplyInsets(View host) {
        WindowInsets insets = getWindowInsets(true /* forceConstruct */);
        final boolean layoutInCutout =
                (mWindowAttributes.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
        if (!layoutInCutout) {
        final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
                == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
        if (!dispatchCutout) {
            // Window is either not laid out in cutout or the status bar inset takes care of
            // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy.
            insets = insets.consumeDisplayCutout();
+102 −17
Original line number Diff line number Diff line
@@ -889,7 +889,12 @@ public interface WindowManager extends ViewManager {
         *  decorations around the border (such as the status bar).  The
         *  window must correctly position its contents to take the screen
         *  decoration into account.  This flag is normally set for you
         *  by Window as described in {@link Window#setFlags}. */
         *  by Window as described in {@link Window#setFlags}.
         *
         *  <p>Note: on displays that have a {@link DisplayCutout}, the window may be placed
         *  such that it avoids the {@link DisplayCutout} area if necessary according to the
         *  {@link #layoutInDisplayCutoutMode}.
         */
        public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;

        /** Window flag: allow window to extend outside of the screen. */
@@ -1299,26 +1304,11 @@ public interface WindowManager extends ViewManager {
        @Retention(RetentionPolicy.SOURCE)
        @LongDef(
            flag = true,
            value = {
                    LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA,
            })
            value = {})
        @interface Flags2 {}

        /**
         * Window flag: allow placing the window within the area that overlaps with the
         * display cutout.
         *
         * <p>
         * The window must correctly position its contents to take the display cutout into account.
         *
         * @see DisplayCutout
         */
        public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001;

        /**
         * Various behavioral options/flags.  Default is none.
         *
         * @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA
         */
        @Flags2 public long flags2;

@@ -2050,6 +2040,77 @@ public interface WindowManager extends ViewManager {
         */
        public boolean hasSystemUiListeners;


        /** @hide */
        @Retention(RetentionPolicy.SOURCE)
        @IntDef(
                flag = true,
                value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,
                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,
                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER})
        @interface LayoutInDisplayCutoutMode {}

        /**
         * Controls how the window is laid out if there is a {@link DisplayCutout}.
         *
         * <p>
         * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
         *
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
         * @see DisplayCutout
         */
        @LayoutInDisplayCutoutMode
        public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;

        /**
         * The window is allowed to extend into the {@link DisplayCutout} area, only if the
         * {@link DisplayCutout} is fully contained within the status bar. Otherwise, the window is
         * laid out such that it does not overlap with the {@link DisplayCutout} area.
         *
         * <p>
         * In practice, this means that if the window did not set FLAG_FULLSCREEN or
         * SYSTEM_UI_FLAG_FULLSCREEN, it can extend into the cutout area in portrait.
         * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does overlap the
         * cutout area.
         *
         * <p>
         * The usual precautions for not overlapping with the status bar are sufficient for ensuring
         * that no important content overlaps with the DisplayCutout.
         *
         * @see DisplayCutout
         * @see WindowInsets
         */
        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;

        /**
         * The window is always allowed to extend into the {@link DisplayCutout} area,
         * even if fullscreen or in landscape.
         *
         * <p>
         * The window must make sure that no important content overlaps with the
         * {@link DisplayCutout}.
         *
         * @see DisplayCutout
         * @see WindowInsets#getDisplayCutout()
         */
        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;

        /**
         * The window is never allowed to overlap with the DisplayCutout area.
         *
         * <p>
         * This should be used with windows that transiently set SYSTEM_UI_FLAG_FULLSCREEN to
         * avoid a relayout of the window when the flag is set or cleared.
         *
         * @see DisplayCutout
         * @see View#SYSTEM_UI_FLAG_FULLSCREEN SYSTEM_UI_FLAG_FULLSCREEN
         * @see View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
         */
        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;


        /**
         * When this window has focus, disable touch pad pointer gesture processing.
         * The window will receive raw position updates from the touch pad instead
@@ -2276,6 +2337,7 @@ public interface WindowManager extends ViewManager {
            out.writeLong(flags2);
            out.writeInt(privateFlags);
            out.writeInt(softInputMode);
            out.writeInt(layoutInDisplayCutoutMode);
            out.writeInt(gravity);
            out.writeFloat(horizontalMargin);
            out.writeFloat(verticalMargin);
@@ -2332,6 +2394,7 @@ public interface WindowManager extends ViewManager {
            flags2 = in.readLong();
            privateFlags = in.readInt();
            softInputMode = in.readInt();
            layoutInDisplayCutoutMode = in.readInt();
            gravity = in.readInt();
            horizontalMargin = in.readFloat();
            verticalMargin = in.readFloat();
@@ -2474,6 +2537,10 @@ public interface WindowManager extends ViewManager {
                softInputMode = o.softInputMode;
                changes |= SOFT_INPUT_MODE_CHANGED;
            }
            if (layoutInDisplayCutoutMode != o.layoutInDisplayCutoutMode) {
                layoutInDisplayCutoutMode = o.layoutInDisplayCutoutMode;
                changes |= LAYOUT_CHANGED;
            }
            if (gravity != o.gravity) {
                gravity = o.gravity;
                changes |= LAYOUT_CHANGED;
@@ -2651,6 +2718,10 @@ public interface WindowManager extends ViewManager {
                sb.append(softInputModeToString(softInputMode));
                sb.append('}');
            }
            if (layoutInDisplayCutoutMode != 0) {
                sb.append(" layoutInDisplayCutoutMode=");
                sb.append(layoutInDisplayCutoutModeToString(layoutInDisplayCutoutMode));
            }
            sb.append(" ty=");
            sb.append(ViewDebug.intToString(LayoutParams.class, "type", type));
            if (format != PixelFormat.OPAQUE) {
@@ -2849,6 +2920,20 @@ public interface WindowManager extends ViewManager {
                    && height == WindowManager.LayoutParams.MATCH_PARENT;
        }

        private static String layoutInDisplayCutoutModeToString(
                @LayoutInDisplayCutoutMode int mode) {
            switch (mode) {
                case LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
                    return "default";
                case LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
                    return "always";
                case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
                    return "never";
                default:
                    return "unknown(" + mode + ")";
            }
        }

        private static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
            final StringBuilder result = new StringBuilder();
            final int state = softInputMode & SOFT_INPUT_MASK_STATE;
+3 −12
Original line number Diff line number Diff line
@@ -16,20 +16,14 @@

package com.android.systemui;

import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import android.content.Context;
import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
@@ -41,9 +35,6 @@ import android.view.WindowManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;

import java.util.Collections;
import java.util.List;

/**
 * Emulates a display cutout by drawing its shape in an overlay as supplied by
 * {@link DisplayCutout}.
@@ -101,7 +92,7 @@ public class EmulatedDisplayCutout extends SystemUI implements ConfigurationList
                PixelFormat.TRANSLUCENT);
        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
        lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        lp.setTitle("EmulatedDisplayCutout");
        lp.gravity = Gravity.TOP;
        return lp;
Loading