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

Commit 21e3f02b authored by shawnlin's avatar shawnlin
Browse files

Make WmDisplayCutout support waterfall and long edge cutout

Bug: 146876976
Test: atest WmTests:WmDisplayCutoutTest
      atest DisplayLayoutTest
Change-Id: I1315533e17bd634f5db6be4276e66a00987dfc3d
parent 6f0d16df
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.util;

import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import android.graphics.Insets;
import android.view.Surface.Rotation;

/**
 * A class containing utility methods related to rotation.
 *
 * @hide
 */
public class RotationUtils {

    /**
     * Rotates an Insets according to the given rotation.
     */
    public static Insets rotateInsets(Insets insets, @Rotation int rotation) {
        if (insets == null || insets == Insets.NONE) {
            return insets;
        }
        Insets rotated;
        switch (rotation) {
            case ROTATION_0:
                rotated = insets;
                break;
            case ROTATION_90:
                rotated = Insets.of(
                        insets.top,
                        insets.right,
                        insets.bottom,
                        insets.left);
                break;
            case ROTATION_180:
                rotated = Insets.of(
                        insets.right,
                        insets.bottom,
                        insets.left,
                        insets.top);
                break;
            case ROTATION_270:
                rotated = Insets.of(
                        insets.bottom,
                        insets.left,
                        insets.top,
                        insets.right);
                break;
            default:
                throw new IllegalArgumentException("unknown rotation: " + rotation);
        }
        return rotated;
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -33,9 +33,11 @@
    <dimen name="toast_y_offset">24dp</dimen>
    <!-- Height of the status bar -->
    <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
    <!-- Height of the status bar in portrait -->
    <!-- Height of the status bar in portrait. The height should be
         Max((status bar content height + waterfall top size), top cutout size) -->
    <dimen name="status_bar_height_portrait">24dp</dimen>
    <!-- Height of the status bar in landscape -->
    <!-- Height of the status bar in landscape. The height should be
         Max((status bar content height + waterfall top size), top cutout size) -->
    <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
    <!-- Height of area above QQS where battery/time go -->
    <dimen name="quick_qs_offset_height">48dp</dimen>
+51 −55
Original line number Diff line number Diff line
@@ -31,9 +31,11 @@ import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
@@ -43,8 +45,6 @@ import android.view.Surface;

import com.android.internal.R;

import java.util.List;

/**
 * Contains information about the layout-properties of a display. This refers to internal layout
 * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
@@ -323,28 +323,38 @@ public class DisplayLayout {
        if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
            return null;
        }
        final Insets waterfallInsets =
                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
        if (rotation == ROTATION_0) {
            return computeSafeInsets(
                    cutout, displayWidth, displayHeight);
            return computeSafeInsets(cutout, displayWidth, displayHeight);
        }
        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
        Rect[] cutoutRects = computeSafeInsets(cutout, displayWidth, displayHeight)
                        .getBoundingRectsAll();
        Rect[] cutoutRects = cutout.getBoundingRectsAll();
        final Rect[] newBounds = new Rect[cutoutRects.length];
        final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight);
        for (int i = 0; i < cutoutRects.length; ++i) {
            newBounds[i] = new Rect(cutoutRects[i]);
            rotateBounds(newBounds[i], displayBounds, rotation);
            final Rect rect = new Rect(cutoutRects[i]);
            if (!rect.isEmpty()) {
                rotateBounds(rect, displayBounds, rotation);
            }
        return computeSafeInsets(DisplayCutout.fromBounds(newBounds),
            newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
        }
        return computeSafeInsets(
                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
                rotated ? displayHeight : displayWidth,
                rotated ? displayWidth : displayHeight);
    }

    private static int getBoundIndexFromRotation(int index, int rotation) {
        return (index - rotation) < 0
                ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH
                : index - rotation;
    }

    /** Calculate safe insets. */
    public static DisplayCutout computeSafeInsets(DisplayCutout inner,
            int displayWidth, int displayHeight) {
        if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
        if (inner == DisplayCutout.NO_CUTOUT) {
            return null;
        }

@@ -353,59 +363,45 @@ public class DisplayLayout {
        return inner.replaceSafeInsets(safeInsets);
    }

    private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
        if (displaySize.getWidth() < displaySize.getHeight()) {
            final List<Rect> boundingRects = cutout.replaceSafeInsets(
                    new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
                    .getBoundingRects();
            int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
            int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
            return new Rect(0, topInset, 0, bottomInset);
        } else if (displaySize.getWidth() > displaySize.getHeight()) {
            final List<Rect> boundingRects = cutout.replaceSafeInsets(
                    new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
                    .getBoundingRects();
            int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
            int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
            return new Rect(leftInset, 0, right, 0);
        } else {
    private static Rect computeSafeInsets(
            Size displaySize, DisplayCutout cutout) {
        if (displaySize.getWidth() == displaySize.getHeight()) {
            throw new UnsupportedOperationException("not implemented: display=" + displaySize
                    + " cutout=" + cutout);
        }

        int leftInset = Math.max(cutout.getWaterfallInsets().left,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
        int topInset = Math.max(cutout.getWaterfallInsets().top,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
        int rightInset = Math.max(cutout.getWaterfallInsets().right,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
                        Gravity.BOTTOM));

        return new Rect(leftInset, topInset, rightInset, bottomInset);
    }

    private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
        if (boundingRect.isEmpty()) {
            return 0;
        }

    private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
        int inset = 0;
        final int size = boundingRects.size();
        for (int i = 0; i < size; i++) {
            Rect boundingRect = boundingRects.get(i);
        switch (gravity) {
            case Gravity.TOP:
                    if (boundingRect.top == 0) {
                        inset = Math.max(inset, boundingRect.bottom);
                    }
                    break;
                return Math.max(inset, boundingRect.bottom);
            case Gravity.BOTTOM:
                    if (boundingRect.bottom == display.getHeight()) {
                        inset = Math.max(inset, display.getHeight() - boundingRect.top);
                    }
                    break;
                return Math.max(inset, display.getHeight() - boundingRect.top);
            case Gravity.LEFT:
                    if (boundingRect.left == 0) {
                        inset = Math.max(inset, boundingRect.right);
                    }
                    break;
                return Math.max(inset, boundingRect.right);
            case Gravity.RIGHT:
                    if (boundingRect.right == display.getWidth()) {
                        inset = Math.max(inset, display.getWidth() - boundingRect.left);
                    }
                    break;
                return Math.max(inset, display.getWidth() - boundingRect.left);
            default:
                throw new IllegalArgumentException("unknown gravity: " + gravity);
        }
    }
        return inset;
    }

    static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) {
        if (displayId == Display.DEFAULT_DISPLAY) {
+6 −4
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ import android.provider.Settings;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.RotationUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -1606,17 +1607,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
            return WmDisplayCutout.NO_CUTOUT;
        }
        final Insets waterfallInsets =
                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
        if (rotation == ROTATION_0) {
            return WmDisplayCutout.computeSafeInsets(
                    cutout, mInitialDisplayWidth, mInitialDisplayHeight);
        }
        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
        final Rect[] newBounds = mRotationUtil.getRotatedBounds(
                WmDisplayCutout.computeSafeInsets(
                        cutout, mInitialDisplayWidth, mInitialDisplayHeight)
                        .getDisplayCutout().getBoundingRectsAll(),
                cutout.getBoundingRectsAll(),
                rotation, mInitialDisplayWidth, mInitialDisplayHeight);
        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
        return WmDisplayCutout.computeSafeInsets(
                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
                rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
                rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
    }
+38 −49
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.util.Size;
import android.view.DisplayCutout;
import android.view.Gravity;

import java.util.List;
import java.util.Objects;

/**
@@ -41,12 +40,17 @@ public class WmDisplayCutout {
        mFrameSize = frameSize;
    }

    public static WmDisplayCutout computeSafeInsets(DisplayCutout inner,
            int displayWidth, int displayHeight) {
        if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
    /**
     * Compute the safe insets according to the given DisplayCutout and the display size.
     *
     * @return return a WmDisplayCutout with calculated safe insets.
     * @hide
     */
    public static WmDisplayCutout computeSafeInsets(
            DisplayCutout inner, int displayWidth, int displayHeight) {
        if (inner == DisplayCutout.NO_CUTOUT) {
            return NO_CUTOUT;
        }

        final Size displaySize = new Size(displayWidth, displayHeight);
        final Rect safeInsets = computeSafeInsets(displaySize, inner);
        return new WmDisplayCutout(inner.replaceSafeInsets(safeInsets), displaySize);
@@ -112,58 +116,43 @@ public class WmDisplayCutout {
    }

    private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
        if (displaySize.getWidth() < displaySize.getHeight()) {
            final List<Rect> boundingRects = cutout.replaceSafeInsets(
                    new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
                    .getBoundingRects();
            int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
            int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
            return new Rect(0, topInset, 0, bottomInset);
        } else if (displaySize.getWidth() > displaySize.getHeight()) {
            final List<Rect> boundingRects = cutout.replaceSafeInsets(
                    new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
                    .getBoundingRects();
            int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
            int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
            return new Rect(leftInset, 0, right, 0);
        } else {
        if (displaySize.getWidth() == displaySize.getHeight()) {
            throw new UnsupportedOperationException("not implemented: display=" + displaySize +
                    " cutout=" + cutout);
        }

        int leftInset = Math.max(cutout.getWaterfallInsets().left,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
        int topInset = Math.max(cutout.getWaterfallInsets().top,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
        int rightInset = Math.max(cutout.getWaterfallInsets().right,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
                findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
                        Gravity.BOTTOM));

        return new Rect(leftInset, topInset, rightInset, bottomInset);
    }

    private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
        if (boundingRect.isEmpty()) {
            return 0;
        }

    private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
        int inset = 0;
        final int size = boundingRects.size();
        for (int i = 0; i < size; i++) {
            Rect boundingRect = boundingRects.get(i);
        switch (gravity) {
            case Gravity.TOP:
                    if (boundingRect.top == 0) {
                        inset = Math.max(inset, boundingRect.bottom);
                    }
                    break;
                return Math.max(inset, boundingRect.bottom);
            case Gravity.BOTTOM:
                    if (boundingRect.bottom == display.getHeight()) {
                        inset = Math.max(inset, display.getHeight() - boundingRect.top);
                    }
                    break;
                return Math.max(inset, display.getHeight() - boundingRect.top);
            case Gravity.LEFT:
                    if (boundingRect.left == 0) {
                        inset = Math.max(inset, boundingRect.right);
                    }
                    break;
                return Math.max(inset, boundingRect.right);
            case Gravity.RIGHT:
                    if (boundingRect.right == display.getWidth()) {
                        inset = Math.max(inset, display.getWidth() - boundingRect.left);
                    }
                    break;
                return Math.max(inset, display.getWidth() - boundingRect.left);
            default:
                throw new IllegalArgumentException("unknown gravity: " + gravity);
        }
    }
        return inset;
    }

    public DisplayCutout getDisplayCutout() {
        return mInner;
Loading