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 * @hide
*/ */
@VisibleForTesting
public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) { public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
Region r = Region.obtain(); Region r = Region.obtain();
r.set(left, top, right, bottom); r.set(left, top, right, bottom);
@@ -422,8 +423,11 @@ public final class DisplayCutout {
m.postTranslate(offsetX, 0); m.postTranslate(offsetX, 0);
p.transform(m); 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) { if (bottomSpec != null) {
final Path bottomPath; final Path bottomPath;
try { try {
@@ -436,10 +440,17 @@ public final class DisplayCutout {
m.postTranslate(0, displayHeight); m.postTranslate(0, displayHeight);
bottomPath.transform(m); bottomPath.transform(m);
p.addPath(bottomPath); 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) { synchronized (CACHE_LOCK) {
sCachedSpec = spec; sCachedSpec = spec;
sCachedDisplayWidth = displayWidth; sCachedDisplayWidth = displayWidth;
@@ -450,12 +461,11 @@ public final class DisplayCutout {
return result; 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 RectF rectF = new RectF();
final Rect rect = new Rect();
p.computeBounds(rectF, false /* unused */); p.computeBounds(rectF, false /* unused */);
rectF.round(rect); rectF.round(inoutRect);
r.op(rect, Op.UNION); inoutRegion.op(inoutRect, Op.UNION);
} }
private static Region boundingRectsToRegion(List<Rect> rects) { private static Region boundingRectsToRegion(List<Rect> rects) {

View File

@@ -2986,6 +2986,10 @@
--> -->
<bool name="config_fillMainBuiltInDisplayCutout">false</bool> <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 --> <!-- Ultrasound support for Mic/speaker path -->
<!-- Whether the default microphone audio source supports near-ultrasound frequencies <!-- Whether the default microphone audio source supports near-ultrasound frequencies
(range of 18 - 21 kHz). --> (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. --> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen> <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. --> <!-- Width of the window of the divider bar used to resize docked stacks. -->
<dimen name="docked_stack_divider_thickness">48dp</dimen> <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_defaultHapticFeedbackIntensity" />
<java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" /> <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_disableApksUnlessMatchedSku_apk_list" />
<java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" /> <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
</resources> </resources>

View File

@@ -19,6 +19,7 @@ package android.view;
import static android.view.DisplayCutout.NO_CUTOUT; import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.DisplayCutout.fromSpec; import static android.view.DisplayCutout.fromSpec;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals; 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))); 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 @Test
public void parcel_unparcel_nocutout() { public void parcel_unparcel_nocutout() {
Parcel p = Parcel.obtain(); Parcel p = Parcel.obtain();

View File

@@ -943,9 +943,9 @@
<dimen name="bottom_padding">48dp</dimen> <dimen name="bottom_padding">48dp</dimen>
<dimen name="edge_margin">8dp</dimen> <dimen name="edge_margin">8dp</dimen>
<dimen name="rounded_corner_radius">0dp</dimen> <dimen name="rounded_corner_radius">@*android:dimen/rounded_corner_radius</dimen>
<dimen name="rounded_corner_radius_top">0dp</dimen> <dimen name="rounded_corner_radius_top">@*android:dimen/rounded_corner_radius_top</dimen>
<dimen name="rounded_corner_radius_bottom">0dp</dimen> <dimen name="rounded_corner_radius_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen> <dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen> <dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</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; 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. * Touch attachment: Display does not receive touch.
*/ */
@@ -453,6 +459,9 @@ final class DisplayDeviceInfo {
if ((flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { if ((flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
msg.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD"); msg.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD");
} }
if ((flags & FLAG_MASK_DISPLAY_CUTOUT) != 0) {
msg.append(", FLAG_MASK_DISPLAY_CUTOUT");
}
return msg.toString(); return msg.toString();
} }
} }

View File

@@ -402,6 +402,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) { && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND; 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.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height); mInfo.width, mInfo.height);
mInfo.type = Display.TYPE_BUILT_IN; mInfo.type = Display.TYPE_BUILT_IN;

View File

@@ -23,6 +23,8 @@ import android.view.DisplayInfo;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -251,14 +253,18 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 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.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address; mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name; mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId; mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
mBaseDisplayInfo.appWidth = deviceInfo.width; mBaseDisplayInfo.appWidth = maskedWidth;
mBaseDisplayInfo.appHeight = deviceInfo.height; mBaseDisplayInfo.appHeight = maskedHeight;
mBaseDisplayInfo.logicalWidth = deviceInfo.width; mBaseDisplayInfo.logicalWidth = maskedWidth;
mBaseDisplayInfo.logicalHeight = deviceInfo.height; mBaseDisplayInfo.logicalHeight = maskedHeight;
mBaseDisplayInfo.rotation = Surface.ROTATION_0; mBaseDisplayInfo.rotation = Surface.ROTATION_0;
mBaseDisplayInfo.modeId = deviceInfo.modeId; mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
@@ -275,19 +281,33 @@ final class LogicalDisplay {
mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos; mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos; mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
mBaseDisplayInfo.state = deviceInfo.state; mBaseDisplayInfo.state = deviceInfo.state;
mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width; mBaseDisplayInfo.smallestNominalAppWidth = maskedWidth;
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height; mBaseDisplayInfo.smallestNominalAppHeight = maskedHeight;
mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width; mBaseDisplayInfo.largestNominalAppWidth = maskedWidth;
mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height; mBaseDisplayInfo.largestNominalAppHeight = maskedHeight;
mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid; mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName; 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; mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo = null; 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 * Applies the layer stack and transformation to the given display device
* so that it shows the contents of this logical display. * 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 physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height; 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. // Determine whether the width or height is more constrained to be scaled.
// physWidth / displayInfo.logicalWidth => letter box // physWidth / displayInfo.logicalWidth => letter box
// or physHeight / displayInfo.logicalHeight => pillar box // or physHeight / displayInfo.logicalHeight => pillar box
@@ -375,6 +401,9 @@ final class LogicalDisplay {
mTempDisplayRect.set(displayRectLeft, displayRectTop, mTempDisplayRect.set(displayRectLeft, displayRectTop,
displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight); displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
// Now add back the offset for the masked area.
mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
mTempDisplayRect.left += mDisplayOffsetX; mTempDisplayRect.left += mDisplayOffsetX;
mTempDisplayRect.right += mDisplayOffsetX; mTempDisplayRect.right += mDisplayOffsetX;
mTempDisplayRect.top += mDisplayOffsetY; mTempDisplayRect.top += mDisplayOffsetY;

View File

@@ -17,6 +17,7 @@
package com.android.server.wm.utils; package com.android.server.wm.utils;
import android.graphics.Rect; import android.graphics.Rect;
import android.view.Surface;
/** /**
* Utility methods to handle insets represented as rects. * Utility methods to handle insets represented as rects.
@@ -26,6 +27,32 @@ public class InsetUtils {
private 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}. * Adds {@code insetsToAdd} to {@code inOutInsets}.
*/ */

View File

@@ -16,6 +16,11 @@
package com.android.server.wm.utils; 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 static junit.framework.Assert.assertEquals;
import android.graphics.Rect; import android.graphics.Rect;
@@ -39,5 +44,29 @@ public class InsetUtilsTest {
InsetUtils.addInsets(rect1, rect2); InsetUtils.addInsets(rect1, rect2);
assertEquals(new Rect(60, 80, 100, 120), rect1); 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;
}
} }