Merge "Cutout: Add developer setting to mask the display cutout" into pi-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -2984,6 +2984,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). -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -939,9 +939,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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user