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

Commit f2bfaebb authored by Shawn Lin's avatar Shawn Lin Committed by Android (Google) Code Review
Browse files

Merge "Make WmDisplayCutout support waterfall and long edge cutout"

parents 6bfbee07 21e3f02b
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
@@ -194,6 +194,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;
@@ -1603,17 +1604,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