From 1cf585059cf44d817f28b072b5f64e6ef616b59c Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Wed, 3 Jan 2018 18:43:27 +0100 Subject: [PATCH] Dispatch DisplayCutout from DisplayAdapter Adds a config resource to configure the main display's cutout and dispatches it from the LocalDisplayAdapter. WindowManager's DisplayContent then transforms it to match the current rotation. Also fixes an issue in EmulatedDisplayCutout where the Path was never reset and changes the color to black. Also fixes the RoundedCorners overlay such that it can layout within the cutout area. Test: atest CoordinateTransformsTest DisplayContentTests Bug: 65689439 Change-Id: If39c8ea3cb55c761517f270dcca292682c0918ad --- core/java/android/view/DisplayCutout.java | 48 +++++++-- core/java/android/view/DisplayInfo.java | 11 +++ core/res/res/values/config.xml | 7 ++ core/res/res/values/symbols.xml | 1 + .../systemui/EmulatedDisplayCutout.java | 6 +- .../com/android/systemui/RoundedCorners.java | 1 + .../server/display/DisplayDeviceInfo.java | 11 +++ .../server/display/LocalDisplayAdapter.java | 14 +++ .../server/display/LogicalDisplay.java | 2 + .../com/android/server/wm/DisplayContent.java | 26 ++++- .../com/android/server/wm/DisplayFrames.java | 43 +++++--- .../server/wm/utils/CoordinateTransforms.java | 62 ++++++++++++ .../server/wm/DisplayContentTests.java | 45 ++++++++- .../wm/utils/CoordinateTransformsTest.java | 98 +++++++++++++++++++ 14 files changed, 348 insertions(+), 27 deletions(-) create mode 100644 services/core/java/com/android/server/wm/utils/CoordinateTransforms.java create mode 100644 services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 3dcfd00bc34c8..1ef5f0950b16f 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -153,7 +153,7 @@ public final class DisplayCutout { @Override public String toString() { return "DisplayCutout{insets=" + mSafeInsets - + " bounds=" + mBounds + + " boundingRect=" + getBoundingRect() + "}"; } @@ -279,9 +279,7 @@ public final class DisplayCutout { * @hide */ public static DisplayCutout fromBoundingPolygon(List points) { - Region bounds = Region.obtain(); Path path = new Path(); - path.reset(); for (int i = 0; i < points.size(); i++) { Point point = points.get(i); @@ -292,14 +290,24 @@ public final class DisplayCutout { } } path.close(); + return fromBounds(path); + } + /** + * Creates an instance from a bounding {@link Path}. + * + * @hide + */ + public static DisplayCutout fromBounds(Path path) { RectF clipRect = new RectF(); path.computeBounds(clipRect, false /* unused */); Region clipRegion = Region.obtain(); clipRegion.set((int) clipRect.left, (int) clipRect.top, (int) clipRect.right, (int) clipRect.bottom); + Region bounds = new Region(); bounds.setPath(path, clipRegion); + clipRegion.recycle(); return new DisplayCutout(ZERO_RECT, bounds); } @@ -329,12 +337,23 @@ public final class DisplayCutout { @Override public void writeToParcel(Parcel out, int flags) { - if (mInner == NO_CUTOUT) { + writeCutoutToParcel(mInner, out, flags); + } + + /** + * Writes a DisplayCutout to a {@link Parcel}. + * + * @see #readCutoutFromParcel(Parcel) + */ + public static void writeCutoutToParcel(DisplayCutout cutout, Parcel out, int flags) { + if (cutout == null) { + out.writeInt(-1); + } else if (cutout == NO_CUTOUT) { out.writeInt(0); } else { out.writeInt(1); - out.writeTypedObject(mInner.mSafeInsets, flags); - out.writeTypedObject(mInner.mBounds, flags); + out.writeTypedObject(cutout.mSafeInsets, flags); + out.writeTypedObject(cutout.mBounds, flags); } } @@ -345,13 +364,13 @@ public final class DisplayCutout { * Needed for AIDL out parameters. */ public void readFromParcel(Parcel in) { - mInner = readCutout(in); + mInner = readCutoutFromParcel(in); } public static final Creator CREATOR = new Creator() { @Override public ParcelableWrapper createFromParcel(Parcel in) { - return new ParcelableWrapper(readCutout(in)); + return new ParcelableWrapper(readCutoutFromParcel(in)); } @Override @@ -360,8 +379,17 @@ public final class DisplayCutout { } }; - private static DisplayCutout readCutout(Parcel in) { - if (in.readInt() == 0) { + /** + * Reads a DisplayCutout from a {@link Parcel}. + * + * @see #writeCutoutToParcel(DisplayCutout, Parcel, int) + */ + public static DisplayCutout readCutoutFromParcel(Parcel in) { + int variant = in.readInt(); + if (variant == -1) { + return null; + } + if (variant == 0) { return NO_CUTOUT; } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index b813ddb63859e..37e9815c93c58 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -148,6 +148,13 @@ public final class DisplayInfo implements Parcelable { */ public int overscanBottom; + /** + * The {@link DisplayCutout} if present, otherwise {@code null}. + * + * @hide + */ + public DisplayCutout displayCutout; + /** * The rotation of the display relative to its natural orientation. * May be one of {@link android.view.Surface#ROTATION_0}, @@ -301,6 +308,7 @@ public final class DisplayInfo implements Parcelable { && overscanTop == other.overscanTop && overscanRight == other.overscanRight && overscanBottom == other.overscanBottom + && Objects.equal(displayCutout, other.displayCutout) && rotation == other.rotation && modeId == other.modeId && defaultModeId == other.defaultModeId @@ -342,6 +350,7 @@ public final class DisplayInfo implements Parcelable { overscanTop = other.overscanTop; overscanRight = other.overscanRight; overscanBottom = other.overscanBottom; + displayCutout = other.displayCutout; rotation = other.rotation; modeId = other.modeId; defaultModeId = other.defaultModeId; @@ -379,6 +388,7 @@ public final class DisplayInfo implements Parcelable { overscanTop = source.readInt(); overscanRight = source.readInt(); overscanBottom = source.readInt(); + displayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(source); rotation = source.readInt(); modeId = source.readInt(); defaultModeId = source.readInt(); @@ -425,6 +435,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(overscanTop); dest.writeInt(overscanRight); dest.writeInt(overscanBottom); + DisplayCutout.ParcelableWrapper.writeCutoutToParcel(displayCutout, dest, flags); dest.writeInt(rotation); dest.writeInt(modeId); dest.writeInt(defaultModeId); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d2685cfb5a8fd..2440e9b431168 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2769,6 +2769,13 @@ some existing device-specific resource overlays. --> @bool/config_windowIsRound + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 22a425174ba07..fb7baf662852e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3198,6 +3198,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java index 6aa465ce9f8c7..a1022604c27f7 100644 --- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java +++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Point; +import android.graphics.PorterDuff; import android.graphics.Region; import android.os.Handler; import android.os.Looper; @@ -114,10 +115,9 @@ public class EmulatedDisplayCutout extends SystemUI { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { + mBounds.reset(); if (insets.getDisplayCutout() != null) { insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds); - } else { - mBounds.reset(); } invalidate(); return insets.consumeDisplayCutout(); @@ -126,7 +126,7 @@ public class EmulatedDisplayCutout extends SystemUI { @Override protected void onDraw(Canvas canvas) { if (!mBounds.isEmpty()) { - mPaint.setColor(Color.DKGRAY); + mPaint.setColor(Color.BLACK); mPaint.setStyle(Paint.Style.FILL); canvas.drawPath(mBounds, mPaint); diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java index b15b79fb984ef..6f7a270799bc0 100644 --- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java +++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java @@ -163,6 +163,7 @@ public class RoundedCorners extends SystemUI implements Tunable { | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; lp.setTitle("RoundedOverlay"); lp.gravity = Gravity.TOP; + lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; return lp; } diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index fddb81ba2af7f..6db3b44c17b28 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.hardware.display.DisplayViewport; import android.util.DisplayMetrics; import android.view.Display; +import android.view.DisplayCutout; import android.view.Surface; import java.util.Arrays; @@ -228,6 +229,11 @@ final class DisplayDeviceInfo { */ public int flags; + /** + * The {@link DisplayCutout} if present or {@code null} otherwise. + */ + public DisplayCutout displayCutout; + /** * The touch attachment, per {@link DisplayViewport#touch}. */ @@ -321,6 +327,7 @@ final class DisplayDeviceInfo { || appVsyncOffsetNanos != other.appVsyncOffsetNanos || presentationDeadlineNanos != other.presentationDeadlineNanos || flags != other.flags + || !Objects.equal(displayCutout, other.displayCutout) || touch != other.touch || rotation != other.rotation || type != other.type @@ -354,6 +361,7 @@ final class DisplayDeviceInfo { appVsyncOffsetNanos = other.appVsyncOffsetNanos; presentationDeadlineNanos = other.presentationDeadlineNanos; flags = other.flags; + displayCutout = other.displayCutout; touch = other.touch; rotation = other.rotation; type = other.type; @@ -380,6 +388,9 @@ final class DisplayDeviceInfo { sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi"); sb.append(", appVsyncOff ").append(appVsyncOffsetNanos); sb.append(", presDeadline ").append(presentationDeadlineNanos); + if (displayCutout != null) { + sb.append(", cutout ").append(displayCutout); + } sb.append(", touch ").append(touchToString(touch)); sb.append(", rotation ").append(rotation); sb.append(", type ").append(Display.typeToString(type)); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index eb9ff589d5f68..483b02c2bf65a 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -30,9 +30,12 @@ import android.os.Looper; import android.os.PowerManager; import android.os.SystemProperties; import android.os.Trace; +import android.text.TextUtils; +import android.util.PathParser; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.Surface; import android.view.SurfaceControl; @@ -400,12 +403,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) { mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND; } + mInfo.displayCutout = parseDefaultDisplayCutout(res); mInfo.type = Display.TYPE_BUILT_IN; mInfo.densityDpi = (int)(phys.density * 160 + 0.5f); mInfo.xDpi = phys.xDpi; mInfo.yDpi = phys.yDpi; mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; } else { + mInfo.displayCutout = null; mInfo.type = Display.TYPE_HDMI; mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; mInfo.name = getContext().getResources().getString( @@ -434,6 +439,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { return mInfo; } + private DisplayCutout parseDefaultDisplayCutout(Resources res) { + String cutoutString = res.getString( + com.android.internal.R.string.config_mainBuiltInDisplayCutout); + if (TextUtils.isEmpty(cutoutString)) { + return null; + } + return DisplayCutout.fromBounds(PathParser.createPathFromPathData(cutoutString)); + } + @Override public Runnable requestDisplayStateLocked(final int state, final int brightness) { // Assume that the brightness is off if the display is being turned off. diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 78a540790db64..132f083c1adc3 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -145,6 +145,7 @@ final class LogicalDisplay { mInfo.overscanRight = mOverrideDisplayInfo.overscanRight; mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom; mInfo.rotation = mOverrideDisplayInfo.rotation; + mInfo.displayCutout = mOverrideDisplayInfo.displayCutout; mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi; mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi; mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi; @@ -280,6 +281,7 @@ final class LogicalDisplay { mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height; mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid; mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName; + mBaseDisplayInfo.displayCutout = deviceInfo.displayCutout; mPrimaryDisplayDeviceInfo = deviceInfo; mInfo = null; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2cc2a0e70ebaa..4c5520fac2cb6 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -62,6 +62,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_A import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; @@ -121,6 +122,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Matrix; +import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -137,6 +139,7 @@ import android.util.MutableBoolean; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; +import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.InputDevice; import android.view.MagnificationSpec; @@ -158,6 +161,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; @@ -208,6 +212,9 @@ class DisplayContent extends WindowContainer 0) { + mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft(); + } + if (c.getSafeInsetTop() > 0) { + mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop(); + } + if (c.getSafeInsetRight() > 0) { + mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight(); + } + if (c.getSafeInsetBottom() > 0) { + mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom(); + } + } } public int getInputMethodWindowVisibleHeight() { @@ -194,8 +217,7 @@ public class DisplayFrames { new Point(height, (screenWidth - widthBottom) / 2), new Point(height, (screenWidth + widthBottom) / 2), new Point(0, (screenWidth + widthTop) / 2) - )).calculateRelativeTo(mUnrestricted); - mDisplayCutoutSafe.left = height; + )); break; case ROTATION_180: mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( @@ -203,8 +225,7 @@ public class DisplayFrames { new Point((screenWidth - widthBottom) / 2, screenHeight - height), new Point((screenWidth + widthBottom) / 2, screenHeight - height), new Point((screenWidth + widthTop) / 2, screenHeight) - )).calculateRelativeTo(mUnrestricted); - mDisplayCutoutSafe.bottom = screenHeight - height; + )); break; case ROTATION_270: mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( @@ -212,8 +233,7 @@ public class DisplayFrames { new Point(screenHeight - height, (screenWidth - widthBottom) / 2), new Point(screenHeight - height, (screenWidth + widthBottom) / 2), new Point(screenHeight, (screenWidth + widthTop) / 2) - )).calculateRelativeTo(mUnrestricted); - mDisplayCutoutSafe.right = screenHeight - height; + )); break; default: mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( @@ -221,8 +241,7 @@ public class DisplayFrames { new Point((screenWidth - widthBottom) / 2, height), new Point((screenWidth + widthBottom) / 2, height), new Point((screenWidth + widthTop) / 2, 0) - )).calculateRelativeTo(mUnrestricted); - mDisplayCutoutSafe.top = height; + )); break; } } diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java new file mode 100644 index 0000000000000..09d7b5de1caf9 --- /dev/null +++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.utils; + +import static android.view.Surface.ROTATION_0; +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.Matrix; +import android.view.Surface.Rotation; + +public class CoordinateTransforms { + + private CoordinateTransforms() { + } + + /** + * 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/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 693264cbe4ee3..2284bbbbf3acd 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -37,13 +37,18 @@ import org.junit.runner.RunWith; import android.annotation.SuppressLint; import android.content.res.Configuration; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.Rect; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.DisplayMetrics; import android.util.SparseIntArray; +import android.view.DisplayCutout; import android.view.MotionEvent; +import android.view.Surface; import java.util.Arrays; import java.util.LinkedList; @@ -53,7 +58,7 @@ import java.util.List; * Tests for the {@link DisplayContent} class. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.DisplayContentTests + * atest com.android.server.wm.DisplayContentTests */ @SmallTest @Presubmit @@ -384,6 +389,38 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(-1, orderedDisplayIds.indexOfValue(dc.getDisplayId())); } + @Test + public void testDisplayCutout_rot0() throws Exception { + synchronized (sWm.getWindowManagerLock()) { + final DisplayContent dc = createNewDisplay(); + dc.mInitialDisplayWidth = 200; + dc.mInitialDisplayHeight = 400; + final DisplayCutout cutout = createCutout(new Rect(80, 0, 120, 10)); + + dc.mInitialDisplayCutout = cutout; + dc.setRotation(Surface.ROTATION_0); + dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. + + assertEquals(cutout, dc.getDisplayInfo().displayCutout); + } + } + + @Test + public void testDisplayCutout_rot90() throws Exception { + synchronized (sWm.getWindowManagerLock()) { + final DisplayContent dc = createNewDisplay(); + dc.mInitialDisplayWidth = 200; + dc.mInitialDisplayHeight = 400; + final DisplayCutout cutout = createCutout(new Rect(80, 0, 120, 10)); + + dc.mInitialDisplayCutout = cutout; + dc.setRotation(Surface.ROTATION_90); + dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. + + assertEquals(createCutout(new Rect(0, 80, 10, 120)), dc.getDisplayInfo().displayCutout); + } + } + @Test @SuppressLint("InlinedApi") public void testOrientationDefinedByKeyguard() { @@ -449,4 +486,10 @@ public class DisplayContentTests extends WindowTestsBase { y, metaState); } + + private DisplayCutout createCutout(Rect r) { + Path p = new Path(); + p.addRect(r.left, r.top, r.right, r.bottom, Path.Direction.CCW); + return DisplayCutout.fromBounds(p); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java new file mode 100644 index 0000000000000..40a10e04c893a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.utils; + +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; + +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.PointF; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; + +public class CoordinateTransformsTest { + + private static final int W = 200; + private static final int H = 400; + + private final Matrix mMatrix = new Matrix(); + + @Rule + public final ErrorCollector mErrorCollector = new ErrorCollector(); + + @Before + public void setUp() throws Exception { + mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef); + } + + @Test + public void transformPhysicalToLogicalCoordinates_rot0() throws Exception { + transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix); + assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX)); + } + + @Test + public void transformPhysicalToLogicalCoordinates_rot90() throws Exception { + transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix); + + checkDevicePoint(0, 0).mapsToLogicalPoint(0, W); + checkDevicePoint(W, H).mapsToLogicalPoint(H, 0); + } + + @Test + public void transformPhysicalToLogicalCoordinates_rot180() throws Exception { + transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix); + + checkDevicePoint(0, 0).mapsToLogicalPoint(W, H); + checkDevicePoint(W, H).mapsToLogicalPoint(0, 0); + } + + @Test + public void transformPhysicalToLogicalCoordinates_rot270() throws Exception { + transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix); + + checkDevicePoint(0, 0).mapsToLogicalPoint(H, 0); + checkDevicePoint(W, H).mapsToLogicalPoint(0, W); + } + + private DevicePointAssertable checkDevicePoint(int x, int y) { + final Point devicePoint = new Point(x, y); + final float[] fs = new float[] {x, y}; + mMatrix.mapPoints(fs); + final PointF transformedPoint = new PointF(fs[0], fs[1]); + + return (expectedX, expectedY) -> { + mErrorCollector.checkThat("t(" + devicePoint + ")", + transformedPoint, is(new PointF(expectedX, expectedY))); + }; + } + + public interface DevicePointAssertable { + void mapsToLogicalPoint(int x, int y); + } +} \ No newline at end of file