Merge "Cutout: Add developer setting to mask the display cutout" into pi-dev

am: c05c2d1e48

Change-Id: I728ce64b53d02f5a75f04071a04f5e6d0f61987c
This commit is contained in:
Adrian Roos
2018-08-21 13:11:53 -07:00
committed by android-build-merger
11 changed files with 156 additions and 19 deletions

View File

@@ -325,6 +325,7 @@ public final class DisplayCutout {
*
* @hide
*/
@VisibleForTesting
public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
Region r = Region.obtain();
r.set(left, top, right, bottom);
@@ -422,8 +423,11 @@ public final class DisplayCutout {
m.postTranslate(offsetX, 0);
p.transform(m);
addToRegion(p, r);
final Rect tmpRect = new Rect();
toRectAndAddToRegion(p, r, tmpRect);
final int topInset = tmpRect.bottom;
final int bottomInset;
if (bottomSpec != null) {
final Path bottomPath;
try {
@@ -436,10 +440,17 @@ public final class DisplayCutout {
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
addToRegion(bottomPath, r);
toRectAndAddToRegion(bottomPath, r, tmpRect);
bottomInset = displayHeight - tmpRect.top;
} else {
bottomInset = 0;
}
final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(r));
// Reuse tmpRect as the inset rect we store into the DisplayCutout instance.
tmpRect.set(0, topInset, 0, bottomInset);
final DisplayCutout cutout = new DisplayCutout(tmpRect, r, false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
sCachedDisplayWidth = displayWidth;
@@ -450,12 +461,11 @@ public final class DisplayCutout {
return result;
}
private static void addToRegion(Path p, Region r) {
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
final RectF rectF = new RectF();
final Rect rect = new Rect();
p.computeBounds(rectF, false /* unused */);
rectF.round(rect);
r.op(rect, Op.UNION);
rectF.round(inoutRect);
inoutRegion.op(inoutRect, Op.UNION);
}
private static Region boundingRectsToRegion(List<Rect> rects) {

View File

@@ -2986,6 +2986,10 @@
-->
<bool name="config_fillMainBuiltInDisplayCutout">false</bool>
<!-- If true, and there is a cutout on the main built in display, the cutout will be masked
by shrinking the display such that it does not overlap the cutout area. -->
<bool name="config_maskMainBuiltInDisplayCutout">false</bool>
<!-- Ultrasound support for Mic/speaker path -->
<!-- Whether the default microphone audio source supports near-ultrasound frequencies
(range of 18 - 21 kHz). -->

View File

@@ -61,6 +61,15 @@
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
<!-- Default radius of the software rounded corners. -->
<dimen name="rounded_corner_radius">0dp</dimen>
<!-- Radius of the software rounded corners at the top of the display in its natural
orientation. If zero, the value of rounded_corner_radius is used. -->
<dimen name="rounded_corner_radius_top">0dp</dimen>
<!-- Radius of the software rounded corners at the bottom of the display in its natural
orientation. If zero, the value of rounded_corner_radius is used. -->
<dimen name="rounded_corner_radius_bottom">0dp</dimen>
<!-- Width of the window of the divider bar used to resize docked stacks. -->
<dimen name="docked_stack_divider_thickness">48dp</dimen>

View File

@@ -3408,6 +3408,8 @@
<java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
<java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
<java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
<java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
</resources>

View File

@@ -19,6 +19,7 @@ package android.view;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.DisplayCutout.fromSpec;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
@@ -219,6 +220,19 @@ public class DisplayCutoutTest {
assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), 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);
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);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
}
@Test
public void parcel_unparcel_nocutout() {
Parcel p = Parcel.obtain();

View File

@@ -943,9 +943,9 @@
<dimen name="bottom_padding">48dp</dimen>
<dimen name="edge_margin">8dp</dimen>
<dimen name="rounded_corner_radius">0dp</dimen>
<dimen name="rounded_corner_radius_top">0dp</dimen>
<dimen name="rounded_corner_radius_bottom">0dp</dimen>
<dimen name="rounded_corner_radius">@*android:dimen/rounded_corner_radius</dimen>
<dimen name="rounded_corner_radius_top">@*android:dimen/rounded_corner_radius_top</dimen>
<dimen name="rounded_corner_radius_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>

View File

@@ -103,6 +103,12 @@ final class DisplayDeviceInfo {
*/
public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
/**
* Flag: The display cutout of this display is masked.
* @hide
*/
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
* Touch attachment: Display does not receive touch.
*/
@@ -453,6 +459,9 @@ final class DisplayDeviceInfo {
if ((flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
msg.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD");
}
if ((flags & FLAG_MASK_DISPLAY_CUTOUT) != 0) {
msg.append(", FLAG_MASK_DISPLAY_CUTOUT");
}
return msg.toString();
}
}

View File

@@ -402,6 +402,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
if (res.getBoolean(
com.android.internal.R.bool.config_maskMainBuiltInDisplayCutout)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
mInfo.type = Display.TYPE_BUILT_IN;

View File

@@ -23,6 +23,8 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
@@ -251,14 +253,18 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
}
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
mBaseDisplayInfo.appWidth = deviceInfo.width;
mBaseDisplayInfo.appHeight = deviceInfo.height;
mBaseDisplayInfo.logicalWidth = deviceInfo.width;
mBaseDisplayInfo.logicalHeight = deviceInfo.height;
mBaseDisplayInfo.appWidth = maskedWidth;
mBaseDisplayInfo.appHeight = maskedHeight;
mBaseDisplayInfo.logicalWidth = maskedWidth;
mBaseDisplayInfo.logicalHeight = maskedHeight;
mBaseDisplayInfo.rotation = Surface.ROTATION_0;
mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
@@ -275,19 +281,33 @@ final class LogicalDisplay {
mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
mBaseDisplayInfo.state = deviceInfo.state;
mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
mBaseDisplayInfo.smallestNominalAppWidth = maskedWidth;
mBaseDisplayInfo.smallestNominalAppHeight = maskedHeight;
mBaseDisplayInfo.largestNominalAppWidth = maskedWidth;
mBaseDisplayInfo.largestNominalAppHeight = maskedHeight;
mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;
mBaseDisplayInfo.displayCutout = deviceInfo.displayCutout;
boolean maskCutout =
(deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo = null;
}
}
/**
* Returns insets in ROTATION_0 for areas that are masked.
*/
private static Rect getMaskingInsets(DisplayDeviceInfo deviceInfo) {
boolean maskCutout = (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
if (maskCutout && deviceInfo.displayCutout != null) {
return deviceInfo.displayCutout.getSafeInsets();
} else {
return new Rect();
}
}
/**
* Applies the layer stack and transformation to the given display device
* so that it shows the contents of this logical display.
@@ -349,6 +369,12 @@ final class LogicalDisplay {
int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
Rect maskingInsets = getMaskingInsets(displayDeviceInfo);
InsetUtils.rotateInsets(maskingInsets, orientation);
// Don't consider the masked area as available when calculating the scaling below.
physWidth -= maskingInsets.left + maskingInsets.right;
physHeight -= maskingInsets.top + maskingInsets.bottom;
// Determine whether the width or height is more constrained to be scaled.
// physWidth / displayInfo.logicalWidth => letter box
// or physHeight / displayInfo.logicalHeight => pillar box
@@ -375,6 +401,9 @@ final class LogicalDisplay {
mTempDisplayRect.set(displayRectLeft, displayRectTop,
displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
// Now add back the offset for the masked area.
mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
mTempDisplayRect.left += mDisplayOffsetX;
mTempDisplayRect.right += mDisplayOffsetX;
mTempDisplayRect.top += mDisplayOffsetY;

View File

@@ -17,6 +17,7 @@
package com.android.server.wm.utils;
import android.graphics.Rect;
import android.view.Surface;
/**
* Utility methods to handle insets represented as rects.
@@ -26,6 +27,32 @@ public class InsetUtils {
private InsetUtils() {
}
/**
* Transforms insets given in one rotation into insets in a different rotation.
*
* @param inOutInsets the insets to transform, is set to the transformed insets
* @param rotationDelta the delta between the new and old rotation.
* Must be one of Surface.ROTATION_0/90/180/270.
*/
public static void rotateInsets(Rect inOutInsets, int rotationDelta) {
final Rect r = inOutInsets;
switch (rotationDelta) {
case Surface.ROTATION_0:
return;
case Surface.ROTATION_90:
r.set(r.top, r.right, r.bottom, r.left);
break;
case Surface.ROTATION_180:
r.set(r.right, r.bottom, r.left, r.top);
break;
case Surface.ROTATION_270:
r.set(r.bottom, r.left, r.top, r.right);
break;
default:
throw new IllegalArgumentException("Unknown rotation: " + rotationDelta);
}
}
/**
* Adds {@code insetsToAdd} to {@code inOutInsets}.
*/

View File

@@ -16,6 +16,11 @@
package com.android.server.wm.utils;
import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static junit.framework.Assert.assertEquals;
import android.graphics.Rect;
@@ -39,5 +44,29 @@ public class InsetUtilsTest {
InsetUtils.addInsets(rect1, rect2);
assertEquals(new Rect(60, 80, 100, 120), rect1);
}
@Test
public void rotate() {
final Rect original = new Rect(1, 2, 3, 4);
assertEquals("rot0", original, rotateCopy(original, ROTATION_0));
final Rect rot90 = rotateCopy(original, ROTATION_90);
assertEquals("rot90", new Rect(2, 3, 4, 1), rot90);
final Rect rot180 = rotateCopy(original, ROTATION_180);
assertEquals("rot180", new Rect(3, 4, 1, 2), rot180);
assertEquals("rot90(rot90)=rot180", rotateCopy(rot90, ROTATION_90), rot180);
final Rect rot270 = rotateCopy(original, ROTATION_270);
assertEquals("rot270", new Rect(4, 1, 2, 3), rot270);
assertEquals("rot90(rot180)=rot270", rotateCopy(rot180, ROTATION_90), rot270);
}
private static Rect rotateCopy(Rect insets, int rotationDelta) {
final Rect copy = new Rect(insets);
InsetUtils.rotateInsets(copy, rotationDelta);
return copy;
}
}