summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shawn Lin <shawnlin@google.com> 2021-01-29 12:42:48 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-01-29 12:42:48 +0000
commit7ee87037f51e651e43c207c67582b36999b3de4a (patch)
tree5438c3027a21d1f50d8ccf7e12de0ff027809311
parent4fb596f006478c955e7bcb785b2bddb99d5e5a6b (diff)
parentf7ff55217c975590b190e3805e5bd6ca87c2b0af (diff)
Merge "Create an API to get a cutout path" into sc-dev
-rw-r--r--core/api/current.txt1
-rw-r--r--core/java/android/util/RotationUtils.java32
-rw-r--r--core/java/android/view/DisplayCutout.java302
-rw-r--r--core/tests/coretests/src/android/view/DisplayCutoutTest.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java10
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java36
8 files changed, 406 insertions, 62 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index ec712d875323..3dd1f598f94f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46182,6 +46182,7 @@ package android.view {
method @NonNull public android.graphics.Rect getBoundingRectRight();
method @NonNull public android.graphics.Rect getBoundingRectTop();
method @NonNull public java.util.List<android.graphics.Rect> getBoundingRects();
+ method @Nullable public android.graphics.Path getCutoutPath();
method public int getSafeInsetBottom();
method public int getSafeInsetLeft();
method public int getSafeInsetRight();
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index a44ed59c14d4..698cb77947cf 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -21,7 +21,9 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import android.annotation.Dimension;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.view.Surface.Rotation;
/**
@@ -69,4 +71,34 @@ public class RotationUtils {
}
return rotated;
}
+
+ /**
+ * Sets a matrix such that given a rotation, it transforms physical display
+ * coordinates to that rotation's logical coordinates.
+ *
+ * @param rotation the rotation to which the matrix should transform
+ * @param out the matrix to be set
+ */
+ public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
+ @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+ switch (rotation) {
+ case ROTATION_0:
+ out.reset();
+ break;
+ case ROTATION_90:
+ out.setRotate(270);
+ out.postTranslate(0, physicalWidth);
+ break;
+ case ROTATION_180:
+ out.setRotate(180);
+ out.postTranslate(physicalWidth, physicalHeight);
+ break;
+ case ROTATION_270:
+ out.setRotate(90);
+ out.postTranslate(physicalHeight, 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rotation);
+ }
+ }
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 525ac534612d..e1a4402d8964 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -23,6 +23,7 @@ import static android.view.DisplayCutoutProto.BOUND_LEFT;
import static android.view.DisplayCutoutProto.BOUND_RIGHT;
import static android.view.DisplayCutoutProto.BOUND_TOP;
import static android.view.DisplayCutoutProto.INSETS;
+import static android.view.Surface.ROTATION_0;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -31,13 +32,16 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Pair;
+import android.util.RotationUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.Surface.Rotation;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -69,6 +73,9 @@ public final class DisplayCutout {
"com.android.internal.display_cutout_emulation";
private static final Rect ZERO_RECT = new Rect();
+ private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo(
+ 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
+ 0 /* rotation */, 0f /* scale */);
/**
* An instance where {@link #isEmpty()} returns {@code true}.
@@ -76,7 +83,7 @@ public final class DisplayCutout {
* @hide
*/
public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
- ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
+ ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, EMPTY_PARSER_INFO,
false /* copyArguments */);
@@ -96,11 +103,15 @@ public final class DisplayCutout {
@GuardedBy("CACHE_LOCK")
private static Insets sCachedWaterfallInsets;
+ @GuardedBy("CACHE_LOCK")
+ private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
+ @GuardedBy("CACHE_LOCK")
+ private static Path sCachedCutoutPath;
+
private final Rect mSafeInsets;
@NonNull
private final Insets mWaterfallInsets;
-
/**
* The bound is at the left of the screen.
* @hide
@@ -210,6 +221,7 @@ public final class DisplayCutout {
}
return result;
}
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
@@ -232,6 +244,106 @@ public final class DisplayCutout {
private final Bounds mBounds;
/**
+ * Stores all the needed info to create the cutout paths.
+ *
+ * @hide
+ */
+ public static class CutoutPathParserInfo {
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+ private final float mDensity;
+ private final String mCutoutSpec;
+ private final @Rotation int mRotation;
+ private final float mScale;
+
+ public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
+ String cutoutSpec, @Rotation int rotation, float scale) {
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ mDensity = density;
+ mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec;
+ mRotation = rotation;
+ mScale = scale;
+ }
+
+ public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) {
+ mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
+ mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
+ mDensity = cutoutPathParserInfo.mDensity;
+ mCutoutSpec = cutoutPathParserInfo.mCutoutSpec;
+ mRotation = cutoutPathParserInfo.mRotation;
+ mScale = cutoutPathParserInfo.mScale;
+ }
+
+ public int getDisplayWidth() {
+ return mDisplayWidth;
+ }
+
+ public int getDisplayHeight() {
+ return mDisplayHeight;
+ }
+
+ public float getDensity() {
+ return mDensity;
+ }
+
+ public @NonNull String getCutoutSpec() {
+ return mCutoutSpec;
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ private boolean hasCutout() {
+ return !mCutoutSpec.isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = result * 48271 + Integer.hashCode(mDisplayWidth);
+ result = result * 48271 + Integer.hashCode(mDisplayHeight);
+ result = result * 48271 + Float.hashCode(mDensity);
+ result = result * 48271 + mCutoutSpec.hashCode();
+ result = result * 48271 + Integer.hashCode(mRotation);
+ result = result * 48271 + Float.hashCode(mScale);
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof CutoutPathParserInfo) {
+ CutoutPathParserInfo c = (CutoutPathParserInfo) o;
+ return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight
+ && mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec)
+ && mRotation == c.mRotation && mScale == c.mScale;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth
+ + " displayHeight=" + mDisplayHeight
+ + " density={" + mDensity + "}"
+ + " cutoutSpec={" + mCutoutSpec + "}"
+ + " rotation={" + mRotation + "}"
+ + " scale={" + mScale + "}"
+ + "}";
+ }
+ }
+
+ private final @NonNull CutoutPathParserInfo mCutoutPathParserInfo;
+
+ /**
* Creates a DisplayCutout instance.
*
* <p>Note that this is only useful for tests. For production code, developers should always
@@ -251,7 +363,8 @@ public final class DisplayCutout {
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) {
- this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true);
+ this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null,
+ true);
}
/**
@@ -276,7 +389,7 @@ public final class DisplayCutout {
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
@NonNull Insets waterfallInsets) {
this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
- true);
+ null, true);
}
/**
@@ -294,7 +407,7 @@ public final class DisplayCutout {
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
@Deprecated
public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
- this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects),
+ this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null,
true /* copyArguments */);
}
@@ -303,28 +416,42 @@ public final class DisplayCutout {
*
* @param safeInsets the insets from each edge which avoid the display cutout as returned by
* {@link #getSafeInsetTop()} etc.
+ * @param waterfallInsets the insets for the curved areas in waterfall display.
+ * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundRight the right bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param info the cutout path parser info.
* @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
* are not copied and MUST remain unchanged forever.
*/
- private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft,
- Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) {
+ private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop,
+ Rect boundRight, Rect boundBottom, CutoutPathParserInfo info,
+ boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
- boolean copyArguments) {
+ CutoutPathParserInfo info, boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(bounds, copyArguments);
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
- private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) {
+ private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
+ CutoutPathParserInfo info) {
mSafeInsets = safeInsets;
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = bounds;
-
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
@@ -534,10 +661,70 @@ public final class DisplayCutout {
return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
}
+ /**
+ * Returns a {@link Path} that contains the cutout paths of all sides on the display.
+ *
+ * To get a cutout path for one specific side, apps can intersect the {@link Path} with the
+ * {@link Rect} obtained from {@link #getBoundingRectLeft()}, {@link #getBoundingRectTop()},
+ * {@link #getBoundingRectRight()} or {@link #getBoundingRectBottom()}.
+ *
+ * @return a {@link Path} contains all the cutout paths based on display coordinate. Returns
+ * null if there is no cutout on the display.
+ */
+ public @Nullable Path getCutoutPath() {
+ if (!mCutoutPathParserInfo.hasCutout()) {
+ return null;
+ }
+ synchronized (CACHE_LOCK) {
+ if (mCutoutPathParserInfo.equals(sCachedCutoutPathParserInfo)) {
+ return sCachedCutoutPath;
+ }
+ }
+ final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(
+ mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(),
+ mCutoutPathParserInfo.getDisplayHeight())
+ .parse(mCutoutPathParserInfo.getCutoutSpec());
+
+ final Path cutoutPath = cutoutSpec.getPath();
+ if (cutoutPath == null || cutoutPath.isEmpty()) {
+ return null;
+ }
+ final Matrix matrix = new Matrix();
+ if (mCutoutPathParserInfo.getRotation() != ROTATION_0) {
+ RotationUtils.transformPhysicalToLogicalCoordinates(
+ mCutoutPathParserInfo.getRotation(),
+ mCutoutPathParserInfo.getDisplayWidth(),
+ mCutoutPathParserInfo.getDisplayHeight(),
+ matrix
+ );
+ }
+ matrix.postScale(mCutoutPathParserInfo.getScale(), mCutoutPathParserInfo.getScale());
+ cutoutPath.transform(matrix);
+
+ synchronized (CACHE_LOCK) {
+ sCachedCutoutPathParserInfo = new CutoutPathParserInfo(mCutoutPathParserInfo);
+ sCachedCutoutPath = cutoutPath;
+ }
+ return cutoutPath;
+ }
+
+ /**
+ * @return the {@link CutoutPathParserInfo};
+ *
+ * @hide
+ */
+ public CutoutPathParserInfo getCutoutPathParserInfo() {
+ return mCutoutPathParserInfo;
+ }
+
@Override
public int hashCode() {
- return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271
- + mWaterfallInsets.hashCode();
+ int result = 0;
+ result = 48271 * result + mSafeInsets.hashCode();
+ result = 48271 * result + mBounds.hashCode();
+ result = 48271 * result + mWaterfallInsets.hashCode();
+ result = 48271 * result + mCutoutPathParserInfo.hashCode();
+ return result;
}
@Override
@@ -548,7 +735,8 @@ public final class DisplayCutout {
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
- && mWaterfallInsets.equals(c.mWaterfallInsets);
+ && mWaterfallInsets.equals(c.mWaterfallInsets)
+ && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo);
}
return false;
}
@@ -558,6 +746,7 @@ public final class DisplayCutout {
return "DisplayCutout{insets=" + mSafeInsets
+ " waterfall=" + mWaterfallInsets
+ " boundingRect={" + mBounds + "}"
+ + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}"
+ "}";
}
@@ -607,7 +796,7 @@ public final class DisplayCutout {
}
return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds,
- false /* copyArguments */);
+ mCutoutPathParserInfo, false /* copyArguments */);
}
private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom,
@@ -638,7 +827,8 @@ public final class DisplayCutout {
* @hide
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
- return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds);
+ return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds,
+ mCutoutPathParserInfo);
}
private static int atLeastZero(int value) {
@@ -658,16 +848,18 @@ public final class DisplayCutout {
for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
}
- return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+ return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null, false /* copyArguments */);
}
/**
- * Creates an instance from a bounding and waterfall insets.
+ * Creates an instance from bounds, waterfall insets and CutoutPathParserInfo.
*
* @hide
*/
- public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) {
- return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */);
+ public static DisplayCutout constructDisplayCutout(Rect[] bounds, Insets waterfallInsets,
+ CutoutPathParserInfo info) {
+ return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, info,
+ false /* copyArguments */);
}
/**
@@ -676,7 +868,8 @@ public final class DisplayCutout {
* @hide
*/
public static DisplayCutout fromBounds(Rect[] bounds) {
- return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+ return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null /* cutoutPathParserInfo */,
+ false /* copyArguments */);
}
/**
@@ -686,10 +879,12 @@ public final class DisplayCutout {
*
* @hide
*/
- public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
- return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
+ public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth,
+ int displayHeight) {
+ return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+ res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- loadWaterfallInset(res));
+ loadWaterfallInset(res)).second;
}
/**
@@ -699,7 +894,7 @@ public final class DisplayCutout {
*/
public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
return pathAndDisplayCutoutFromSpec(
- res.getString(R.string.config_mainBuiltInDisplayCutout),
+ res.getString(R.string.config_mainBuiltInDisplayCutout), null,
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
loadWaterfallInset(res)).first;
}
@@ -710,14 +905,30 @@ public final class DisplayCutout {
* @hide
*/
@VisibleForTesting(visibility = PRIVATE)
- public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
- float density, Insets waterfallInsets) {
+ public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
+ int displayHeight, float density, Insets waterfallInsets) {
return pathAndDisplayCutoutFromSpec(
- spec, displayWidth, displayHeight, density, waterfallInsets).second;
+ pathSpec, null, displayWidth, displayHeight, density, waterfallInsets)
+ .second;
}
- private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
- int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
+ /**
+ * Gets the cutout path and the corresponding DisplayCutout instance from the spec string.
+ *
+ * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
+ * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+ * @param displayWidth the display width.
+ * @param displayHeight the display height.
+ * @param density the display density.
+ * @param waterfallInsets the waterfall insets of the display.
+ * @return a Pair contains the cutout path and the corresponding DisplayCutout instance.
+ */
+ private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
+ String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density,
+ Insets waterfallInsets) {
+ // Always use the rect approximation spec to create the cutout if it's not null because
+ // transforming and sending a Region constructed from a path is very costly.
+ String spec = rectSpec != null ? rectSpec : pathSpec;
if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) {
return NULL_PAIR;
}
@@ -750,9 +961,12 @@ public final class DisplayCutout {
Math.max(waterfallInsets.bottom, safeInset.bottom));
}
+ final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth,
+ displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */);
+
final DisplayCutout cutout = new DisplayCutout(
- safeInset, waterfallInsets, boundLeft, boundTop,
- boundRight, boundBottom, false /* copyArguments */);
+ safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
+ cutoutPathParserInfo , false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
@@ -817,6 +1031,12 @@ public final class DisplayCutout {
out.writeTypedObject(cutout.mSafeInsets, flags);
out.writeTypedArray(cutout.mBounds.getRects(), flags);
out.writeTypedObject(cutout.mWaterfallInsets, flags);
+ out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth());
+ out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight());
+ out.writeFloat(cutout.mCutoutPathParserInfo.getDensity());
+ out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec());
+ out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
+ out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
}
}
@@ -860,9 +1080,17 @@ public final class DisplayCutout {
Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
in.readTypedArray(bounds, Rect.CREATOR);
Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
+ int displayWidth = in.readInt();
+ int displayHeight = in.readInt();
+ float density = in.readFloat();
+ String cutoutSpec = in.readString();
+ int rotation = in.readInt();
+ float scale = in.readFloat();
+ final CutoutPathParserInfo info = new CutoutPathParserInfo(
+ displayWidth, displayHeight, density, cutoutSpec, rotation, scale);
return new DisplayCutout(
- safeInsets, waterfallInsets, bounds, false /* copyArguments */);
+ safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
}
public DisplayCutout get() {
@@ -884,7 +1112,15 @@ public final class DisplayCutout {
bounds.scale(scale);
final Rect waterfallInsets = mInner.mWaterfallInsets.toRect();
waterfallInsets.scale(scale);
- mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds);
+ final CutoutPathParserInfo info = new CutoutPathParserInfo(
+ mInner.mCutoutPathParserInfo.getDisplayWidth(),
+ mInner.mCutoutPathParserInfo.getDisplayHeight(),
+ mInner.mCutoutPathParserInfo.getDensity(),
+ mInner.mCutoutPathParserInfo.getCutoutSpec(),
+ mInner.mCutoutPathParserInfo.getRotation(),
+ scale);
+
+ mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
}
@Override
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index d02c6d588585..a5261aecdbfb 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -22,6 +22,8 @@ import static android.view.DisplayCutout.fromSpec;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -30,6 +32,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import android.graphics.Insets;
+import android.graphics.Path;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -130,6 +133,8 @@ public class DisplayCutoutTest {
@Test
public void testHasCutout_noCutout() throws Exception {
assertTrue(NO_CUTOUT.isBoundsEmpty());
+ assertThat(NO_CUTOUT.getWaterfallInsets(), equalTo(Insets.NONE));
+ assertThat(NO_CUTOUT.getCutoutPath(), nullValue());
}
@Test
@@ -165,6 +170,59 @@ public class DisplayCutoutTest {
}
@Test
+ public void testGetCutoutPath() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE);
+ assertThat(cutout.getCutoutPath(), notNullValue());
+ }
+
+ @Test
+ public void testGetCutoutPath_caches() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ assertThat(first, equalTo(second));
+ }
+
+ @Test
+ public void testGetCutoutPath_wontCacheIfCutoutPathParerInfoChanged() throws Exception {
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ assertThat(first, not(equalTo(second)));
+ }
+
+ @Test
+ public void testGetCutoutPathParserInfo() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE);
+ assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
+ assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
+ assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
+ assertThat(cutoutSpecString.trim(),
+ equalTo(cutout.getCutoutPathParserInfo().getCutoutSpec()));
+ assertThat(0, equalTo(cutout.getCutoutPathParserInfo().getRotation()));
+ assertThat(1f, equalTo(cutout.getCutoutPathParserInfo().getScale()));
+ }
+
+ @Test
public void testHashCode() throws Exception {
assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode());
assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 3181dbf74ace..58a4baf39614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -356,11 +356,11 @@ 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);
}
+ final Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
Rect[] cutoutRects = cutout.getBoundingRectsAll();
final Rect[] newBounds = new Rect[cutoutRects.length];
@@ -372,8 +372,12 @@ public class DisplayLayout {
}
newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
}
+ final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+ final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+ info.getCutoutSpec(), rotation, info.getScale());
return computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+ DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? displayHeight : displayWidth,
rotated ? displayWidth : displayHeight);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f293fc373bfe..73bcf47979fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -190,6 +190,7 @@ import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayCutout.CutoutPathParserInfo;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
@@ -1934,18 +1935,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
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 Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final Rect[] newBounds = mRotationUtil.getRotatedBounds(
cutout.getBoundingRectsAll(),
rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+ final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+ info.getCutoutSpec(), rotation, info.getScale());
return WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+ DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 78dd4b8119e3..4bea9a2eea45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -27,7 +27,6 @@ import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -96,6 +95,7 @@ import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.metrics.LogMaker;
@@ -707,6 +707,7 @@ public class DisplayContentTests extends WindowTestsBase {
// same width and height.
final int displayWidth = dc.mInitialDisplayWidth;
final int displayHeight = dc.mInitialDisplayHeight;
+ final float density = dc.mInitialDisplayDensity;
final int cutoutWidth = 40;
final int cutoutHeight = 10;
final int left = (displayWidth - cutoutWidth) / 2;
@@ -714,9 +715,13 @@ public class DisplayContentTests extends WindowTestsBase {
final int right = (displayWidth + cutoutWidth) / 2;
final int bottom = cutoutHeight;
- final Rect r1 = new Rect(left, top, right, bottom);
+ final Rect zeroRect = new Rect();
+ final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
+ zeroRect};
+ final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
+ displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+ DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -731,9 +736,12 @@ public class DisplayContentTests extends WindowTestsBase {
// | | ---o
// | | |
// | | -------------
- final Rect r = new Rect(top, left, bottom, right);
+ final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
+ zeroRect};
+ final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
+ displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
assertEquals(new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
+ DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
.computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
dc.getDisplayInfo().displayCutout);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 39976a5a2af1..b2646f281eb9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -150,9 +150,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_waterfall() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(1, 2, 3, 4)),
+ Insets.of(1, 2, 3, 4), null),
200, 400);
assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets());
@@ -161,9 +161,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
- Insets.of(0, 20, 0, 0)),
+ Insets.of(0, 20, 0, 0), null),
200, 400);
assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -172,9 +172,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
- Insets.of(0, 40, 0, 0)),
+ Insets.of(0, 40, 0, 0), null),
200, 400);
assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -183,9 +183,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(20, 0, 0, 0)),
+ Insets.of(20, 0, 0, 0), null),
200, 400);
assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -194,9 +194,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(40, 0, 0, 0)),
+ Insets.of(40, 0, 0, 0), null),
200, 400);
assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -205,9 +205,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
- Insets.of(0, 0, 0, 20)),
+ Insets.of(0, 0, 0, 20), null),
200, 400);
assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets());
@@ -216,9 +216,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
- Insets.of(0, 0, 0, 40)),
+ Insets.of(0, 0, 0, 40), null),
200, 400);
assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets());
@@ -227,9 +227,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
- Insets.of(0, 0, 20, 0)),
+ Insets.of(0, 0, 20, 0), null),
200, 400);
assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -238,9 +238,9 @@ public class WmDisplayCutoutTest {
@Test
public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
- Insets.of(0, 0, 40, 0)),
+ Insets.of(0, 0, 40, 0), null),
200, 400);
assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets());