Merge "Make DisplayCutout support waterfall insets"

This commit is contained in:
Shawn Lin
2020-01-19 09:57:54 +00:00
committed by Android (Google) Code Review
3 changed files with 303 additions and 113 deletions

View File

@@ -50525,6 +50525,7 @@ package android.view {
public final class DisplayCutout {
ctor public DisplayCutout(@NonNull android.graphics.Insets, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect);
ctor public DisplayCutout(@NonNull android.graphics.Insets, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, @NonNull android.graphics.Insets);
ctor @Deprecated public DisplayCutout(@Nullable android.graphics.Rect, @Nullable java.util.List<android.graphics.Rect>);
method @NonNull public android.graphics.Rect getBoundingRectBottom();
method @NonNull public android.graphics.Rect getBoundingRectLeft();
@@ -50535,6 +50536,7 @@ package android.view {
method public int getSafeInsetLeft();
method public int getSafeInsetRight();
method public int getSafeInsetTop();
method @NonNull public android.graphics.Insets getWaterfallInsets();
}
public final class DragAndDropPermissions implements android.os.Parcelable {

View File

@@ -86,7 +86,7 @@ public final class DisplayCutout {
* @hide
*/
public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
false /* copyArguments */);
@@ -103,8 +103,12 @@ public final class DisplayCutout {
private static float sCachedDensity;
@GuardedBy("CACHE_LOCK")
private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
@GuardedBy("CACHE_LOCK")
private static Insets sCachedWaterfallInsets;
private final Rect mSafeInsets;
@NonNull
private final Insets mWaterfallInsets;
/**
@@ -251,7 +255,32 @@ 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(), boundLeft, boundTop, boundRight, boundBottom, true);
this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true);
}
/**
* Creates a DisplayCutout instance.
*
* <p>Note that this is only useful for tests. For production code, developers should always
* use a {@link DisplayCutout} obtained from the system.</p>
*
* @param safeInsets the insets from each edge which avoid the display cutout as returned by
* {@link #getSafeInsetTop()} etc.
* @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 waterfallInsets the insets for the curved areas in waterfall display.
*/
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
@NonNull Insets waterfallInsets) {
this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
true);
}
/**
@@ -269,7 +298,7 @@ public final class DisplayCutout {
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
@Deprecated
public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
this(safeInsets, extractBoundsFromList(safeInsets, boundingRects),
this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects),
true /* copyArguments */);
}
@@ -281,19 +310,23 @@ public final class DisplayCutout {
* @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, Rect boundLeft, Rect boundTop, Rect boundRight,
Rect boundBottom, boolean copyArguments) {
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft,
Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
}
private DisplayCutout(Rect safeInsets, Rect[] bounds, boolean copyArguments) {
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(bounds, copyArguments);
}
private DisplayCutout(Rect safeInsets, Bounds bounds) {
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) {
mSafeInsets = safeInsets;
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = bounds;
}
@@ -308,6 +341,14 @@ public final class DisplayCutout {
}
}
/**
* Return the waterfall insets.
*/
public @NonNull Insets getWaterfallInsets() {
return mWaterfallInsets;
}
/**
* Find the position of the bounding rect, and create an array of Rect whose index represents
* the position (= BoundsPosition).
@@ -476,7 +517,8 @@ public final class DisplayCutout {
@Override
public int hashCode() {
return mSafeInsets.hashCode() * 48271 + mBounds.hashCode();
return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271
+ mWaterfallInsets.hashCode();
}
@Override
@@ -486,7 +528,8 @@ public final class DisplayCutout {
}
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds);
return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
&& mWaterfallInsets.equals(c.mWaterfallInsets);
}
return false;
}
@@ -494,6 +537,7 @@ public final class DisplayCutout {
@Override
public String toString() {
return "DisplayCutout{insets=" + mSafeInsets
+ " waterfall=" + mWaterfallInsets
+ " boundingRect={" + mBounds + "}"
+ "}";
}
@@ -508,6 +552,7 @@ public final class DisplayCutout {
mBounds.getRect(BOUNDS_POSITION_TOP).dumpDebug(proto, BOUND_TOP);
mBounds.getRect(BOUNDS_POSITION_RIGHT).dumpDebug(proto, BOUND_RIGHT);
mBounds.getRect(BOUNDS_POSITION_BOTTOM).dumpDebug(proto, BOUND_BOTTOM);
mWaterfallInsets.toRect().dumpDebug(proto, INSETS);
proto.end(token);
}
@@ -553,7 +598,7 @@ public final class DisplayCutout {
}
}
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
return new DisplayCutout(safeInsets, mWaterfallInsets, bounds, false /* copyArguments */);
}
/**
@@ -565,7 +610,7 @@ public final class DisplayCutout {
* @hide
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
return new DisplayCutout(new Rect(safeInsets), mBounds);
return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds);
}
private static int atLeastZero(int value) {
@@ -585,7 +630,16 @@ 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, bounds, false /* copyArguments */);
return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
}
/**
* Creates an instance from a bounding and waterfall insets.
*
* @hide
*/
public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) {
return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */);
}
/**
@@ -594,7 +648,7 @@ public final class DisplayCutout {
* @hide
*/
public static DisplayCutout fromBounds(Rect[] bounds) {
return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
}
/**
@@ -606,7 +660,8 @@ public final class DisplayCutout {
*/
public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT);
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
loadWaterfallInset(res));
}
/**
@@ -617,7 +672,8 @@ public final class DisplayCutout {
public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
return pathAndDisplayCutoutFromSpec(
res.getString(R.string.config_mainBuiltInDisplayCutout),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT).first;
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
loadWaterfallInset(res)).first;
}
/**
@@ -627,91 +683,109 @@ public final class DisplayCutout {
*/
@VisibleForTesting(visibility = PRIVATE)
public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
float density) {
return pathAndDisplayCutoutFromSpec(spec, displayWidth, displayHeight, density).second;
float density, Insets waterfallInsets) {
return pathAndDisplayCutoutFromSpec(
spec, displayWidth, displayHeight, density, waterfallInsets).second;
}
private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
int displayWidth, int displayHeight, float density) {
if (TextUtils.isEmpty(spec)) {
int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) {
return NULL_PAIR;
}
synchronized (CACHE_LOCK) {
if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
&& sCachedDisplayHeight == displayHeight
&& sCachedDensity == density) {
&& sCachedDensity == density
&& waterfallInsets.equals(sCachedWaterfallInsets)) {
return sCachedCutout;
}
}
spec = spec.trim();
final float offsetX;
if (spec.endsWith(RIGHT_MARKER)) {
offsetX = displayWidth;
spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
} else if (spec.endsWith(LEFT_MARKER)) {
offsetX = 0;
spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
} else {
offsetX = displayWidth / 2f;
}
final boolean inDp = spec.endsWith(DP_MARKER);
if (inDp) {
spec = spec.substring(0, spec.length() - DP_MARKER.length());
}
String bottomSpec = null;
if (spec.contains(BOTTOM_MARKER)) {
String[] splits = spec.split(BOTTOM_MARKER, 2);
spec = splits[0].trim();
bottomSpec = splits[1].trim();
}
final Path p;
final Region r = Region.obtain();
try {
p = PathParser.createPathFromPathData(spec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate cutout: ", e);
return NULL_PAIR;
}
final Matrix m = new Matrix();
if (inDp) {
m.postScale(density, density);
}
m.postTranslate(offsetX, 0);
p.transform(m);
Rect boundTop = new Rect();
toRectAndAddToRegion(p, r, boundTop);
final int topInset = boundTop.bottom;
Path p = null;
Rect boundTop = null;
Rect boundBottom = null;
final int bottomInset;
if (bottomSpec != null) {
final Path bottomPath;
try {
bottomPath = PathParser.createPathFromPathData(bottomSpec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
return NULL_PAIR;
Rect safeInset = new Rect();
String bottomSpec = null;
if (!TextUtils.isEmpty(spec)) {
spec = spec.trim();
final float offsetX;
if (spec.endsWith(RIGHT_MARKER)) {
offsetX = displayWidth;
spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
} else if (spec.endsWith(LEFT_MARKER)) {
offsetX = 0;
spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
} else {
offsetX = displayWidth / 2f;
}
final boolean inDp = spec.endsWith(DP_MARKER);
if (inDp) {
spec = spec.substring(0, spec.length() - DP_MARKER.length());
}
if (spec.contains(BOTTOM_MARKER)) {
String[] splits = spec.split(BOTTOM_MARKER, 2);
spec = splits[0].trim();
bottomSpec = splits[1].trim();
}
final Matrix m = new Matrix();
final Region r = Region.obtain();
if (!spec.isEmpty()) {
try {
p = PathParser.createPathFromPathData(spec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate cutout: ", e);
}
if (p != null) {
if (inDp) {
m.postScale(density, density);
}
m.postTranslate(offsetX, 0);
p.transform(m);
boundTop = new Rect();
toRectAndAddToRegion(p, r, boundTop);
safeInset.top = boundTop.bottom;
}
}
if (bottomSpec != null) {
int bottomInset = 0;
Path bottomPath = null;
try {
bottomPath = PathParser.createPathFromPathData(bottomSpec);
} catch (Throwable e) {
Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
}
if (bottomPath != null) {
// Keep top transform
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
boundBottom = new Rect();
toRectAndAddToRegion(bottomPath, r, boundBottom);
bottomInset = displayHeight - boundBottom.top;
}
safeInset.bottom = bottomInset;
}
// Keep top transform
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
boundBottom = new Rect();
toRectAndAddToRegion(bottomPath, r, boundBottom);
bottomInset = displayHeight - boundBottom.top;
} else {
bottomInset = 0;
}
Rect safeInset = new Rect(0, topInset, 0, bottomInset);
final DisplayCutout cutout = new DisplayCutout(
safeInset, null /* boundLeft */, boundTop, null /* boundRight */, boundBottom,
false /* copyArguments */);
if (!waterfallInsets.equals(Insets.NONE)) {
safeInset.set(
Math.max(waterfallInsets.left, safeInset.left),
Math.max(waterfallInsets.top, safeInset.top),
Math.max(waterfallInsets.right, safeInset.right),
Math.max(waterfallInsets.bottom, safeInset.bottom));
}
final DisplayCutout cutout = new DisplayCutout(
safeInset, waterfallInsets, null /* boundLeft */, boundTop,
null /* boundRight */, boundBottom, false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
@@ -719,6 +793,7 @@ public final class DisplayCutout {
sCachedDisplayHeight = displayHeight;
sCachedDensity = density;
sCachedCutout = result;
sCachedWaterfallInsets = waterfallInsets;
}
return result;
}
@@ -730,6 +805,15 @@ public final class DisplayCutout {
inoutRegion.op(inoutRect, Op.UNION);
}
private static Insets loadWaterfallInset(Resources res) {
return Insets.of(
res.getDimensionPixelSize(R.dimen.waterfall_display_left_edge_size),
res.getDimensionPixelSize(R.dimen.waterfall_display_top_edge_size),
res.getDimensionPixelSize(R.dimen.waterfall_display_right_edge_size),
res.getDimensionPixelSize(R.dimen.waterfall_display_bottom_edge_size));
}
/**
* Helper class for passing {@link DisplayCutout} through binder.
*
@@ -773,6 +857,7 @@ public final class DisplayCutout {
out.writeInt(1);
out.writeTypedObject(cutout.mSafeInsets, flags);
out.writeTypedArray(cutout.mBounds.getRects(), flags);
out.writeTypedObject(cutout.mWaterfallInsets, flags);
}
}
@@ -815,8 +900,10 @@ public final class DisplayCutout {
Rect safeInsets = in.readTypedObject(Rect.CREATOR);
Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
in.readTypedArray(bounds, Rect.CREATOR);
Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
return new DisplayCutout(
safeInsets, waterfallInsets, bounds, false /* copyArguments */);
}
public DisplayCutout get() {

View File

@@ -69,6 +69,8 @@ public class DisplayCutoutTest {
null /* boundBottom */);
final DisplayCutout mCutoutTop = createCutoutTop();
final DisplayCutout mCutoutWithWaterfall = createCutoutWithWaterfall();
final DisplayCutout mWaterfallOnly = createCutoutWaterfallOnly();
@Test
public void testExtractBoundsFromList_left() {
@@ -126,9 +128,23 @@ public class DisplayCutoutTest {
}
@Test
public void hasCutout() throws Exception {
assertTrue(NO_CUTOUT.isEmpty());
assertFalse(mCutoutTop.isEmpty());
public void testHasCutout_noCutout() throws Exception {
assertTrue(NO_CUTOUT.isBoundsEmpty());
}
@Test
public void testHasCutout_cutoutOnly() {
assertFalse(mCutoutTop.isBoundsEmpty());
}
@Test
public void testHasCutout_cutoutWithWaterfall() {
assertFalse(mCutoutWithWaterfall.isBoundsEmpty());
}
@Test
public void testHasCutout_waterfallOnly() {
assertTrue(mWaterfallOnly.isBoundsEmpty());
}
@Test
@@ -141,21 +157,28 @@ public class DisplayCutoutTest {
assertEquals(new Rect(5, 6, 7, 8), mCutoutNumbers.getSafeInsets());
}
@Test
public void testGetWaterfallInsets() throws Exception {
DisplayCutout cutout =
createCutoutWaterfallOnly(Insets.of(5, 6, 7, 8));
assertEquals(Insets.of(5, 6, 7, 8), cutout.getWaterfallInsets());
}
@Test
public void testHashCode() throws Exception {
assertEquals(mCutoutTop.hashCode(), createCutoutTop().hashCode());
assertNotEquals(mCutoutTop.hashCode(), mCutoutNumbers.hashCode());
assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode());
assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode());
}
@Test
public void testEquals() throws Exception {
assertEquals(mCutoutTop, createCutoutTop());
assertNotEquals(mCutoutTop, mCutoutNumbers);
assertEquals(mCutoutWithWaterfall, createCutoutWithWaterfall());
assertNotEquals(mCutoutWithWaterfall, mCutoutNumbers);
}
@Test
public void testToString() throws Exception {
assertFalse(mCutoutTop.toString().isEmpty());
assertFalse(mCutoutWithWaterfall.toString().isEmpty());
assertFalse(mCutoutNumbers.toString().isEmpty());
}
@@ -240,12 +263,12 @@ public class DisplayCutoutTest {
public void parcel_unparcel_regular() {
Parcel p = Parcel.obtain();
new ParcelableWrapper(mCutoutTop).writeToParcel(p, 0);
new ParcelableWrapper(mCutoutWithWaterfall).writeToParcel(p, 0);
int posAfterWrite = p.dataPosition();
p.setDataPosition(0);
assertEquals(mCutoutTop, ParcelableWrapper.CREATOR.createFromParcel(p).get());
assertEquals(mCutoutWithWaterfall, ParcelableWrapper.CREATOR.createFromParcel(p).get());
assertEquals(posAfterWrite, p.dataPosition());
}
@@ -264,44 +287,64 @@ public class DisplayCutoutTest {
@Test
public void fromSpec_caches() {
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), sameInstance(cached));
Insets waterfallInsets = Insets.of(0, 20, 0, 20);
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets),
sameInstance(cached));
}
@Test
public void fromSpec_wontCacheIfSpecChanges() {
DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfScreenWidthChanges() {
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfScreenHeightChanges() {
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfDensityChanges() {
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f);
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfWaterfallInsetsChange() {
Insets waterfallInsets = Insets.of(0, 20, 0, 20);
DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
assertThat(
fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets),
not(sameInstance(cached)));
}
@Test
public void fromSpec_setsSafeInsets_top() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f);
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f, Insets.NONE);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 0)));
}
@Test
public void fromSpec_setsSafeInsets_top_and_bottom() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+ "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f);
+ "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
ZERO_RECT, new Rect(50, 0, 150, 20),
@@ -309,6 +352,38 @@ public class DisplayCutoutTest {
}));
}
@Test
public void fromSpec_setsSafeInsets_waterfallTopBottom() {
DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30));
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
}
@Test
public void fromSpec_setsSafeInsets_waterfallLeftRight() {
DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0));
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 0, 30, 0)));
}
@Test
public void fromSpec_setsSafeInsets_waterfall_allEdges() {
DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30));
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 30, 30, 30)));
}
@Test
public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallTopBottom() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+ "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(0, 30, 0, 30));
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
}
@Test
public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallLeftRight() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+ "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(30, 0, 30, 0));
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 20, 30, 20)));
}
@Test
public void parcel_unparcel_nocutout() {
Parcel p = Parcel.obtain();
@@ -326,7 +401,7 @@ public class DisplayCutoutTest {
public void parcel_unparcel_inplace() {
Parcel p = Parcel.obtain();
new ParcelableWrapper(mCutoutTop).writeToParcel(p, 0);
new ParcelableWrapper(mCutoutWithWaterfall).writeToParcel(p, 0);
int posAfterWrite = p.dataPosition();
p.setDataPosition(0);
@@ -334,22 +409,24 @@ public class DisplayCutoutTest {
ParcelableWrapper wrapper = new ParcelableWrapper();
wrapper.readFromParcel(p);
assertEquals(mCutoutTop, wrapper.get());
assertEquals(mCutoutWithWaterfall, wrapper.get());
assertEquals(posAfterWrite, p.dataPosition());
}
@Test
public void wrapper_hashcode() throws Exception {
assertEquals(new ParcelableWrapper(mCutoutTop).hashCode(),
new ParcelableWrapper(createCutoutTop()).hashCode());
assertNotEquals(new ParcelableWrapper(mCutoutTop).hashCode(),
assertEquals(new ParcelableWrapper(mCutoutWithWaterfall).hashCode(),
new ParcelableWrapper(createCutoutWithWaterfall()).hashCode());
assertNotEquals(new ParcelableWrapper(mCutoutWithWaterfall).hashCode(),
new ParcelableWrapper(mCutoutNumbers).hashCode());
}
@Test
public void wrapper_equals() throws Exception {
assertEquals(new ParcelableWrapper(mCutoutTop), new ParcelableWrapper(createCutoutTop()));
assertNotEquals(new ParcelableWrapper(mCutoutTop), new ParcelableWrapper(mCutoutNumbers));
assertEquals(new ParcelableWrapper(mCutoutWithWaterfall),
new ParcelableWrapper(createCutoutWithWaterfall()));
assertNotEquals(new ParcelableWrapper(mCutoutWithWaterfall),
new ParcelableWrapper(mCutoutNumbers));
}
private static DisplayCutout createCutoutTop() {
@@ -363,4 +440,28 @@ public class DisplayCutoutTest {
safeInset, null /* boundLeft */, boundTop, null /* boundRight */,
null /* boundBottom */);
}
private static DisplayCutout createCutoutWithWaterfall() {
return new DisplayCutout(
Insets.of(20, 100, 20, 0),
ZERO_RECT,
new Rect(50, 0, 75, 100),
ZERO_RECT,
ZERO_RECT,
Insets.of(20, 0, 20, 0));
}
private static DisplayCutout createCutoutWaterfallOnly() {
return createCutoutWaterfallOnly(Insets.of(20, 0, 20, 0));
}
private static DisplayCutout createCutoutWaterfallOnly(Insets waterfallInsets) {
return new DisplayCutout(
Insets.of(20, 0, 20, 0),
ZERO_RECT,
ZERO_RECT,
ZERO_RECT,
ZERO_RECT,
waterfallInsets);
}
}