From 34cedd485795284c9084c7683f355fd3c0216238 Mon Sep 17 00:00:00 2001 From: Evan Laird Date: Mon, 15 Jun 2020 18:07:32 -0400 Subject: [PATCH] Allow separate top and bottom rounded.xml layouts ScreenDecorations expects that the overlays for the top and bottom (containing the rounded corners and potentially display cutout) will have the exact same drawable on all 4 corners. This change allows for two separate layouts divided into top and bottom. Test: manual Bug: 156017682 Change-Id: Icbfaf66b19a6d154dd67ce2d9bc013a29778c329 --- .../res/drawable/rounded_corner_bottom.xml | 16 +++ .../res/drawable/rounded_corner_top.xml | 16 +++ .../SystemUI/res/layout/rounded_corners.xml | 1 + .../res/layout/rounded_corners_bottom.xml | 36 ++++++ .../res/layout/rounded_corners_top.xml | 36 ++++++ .../android/systemui/ScreenDecorations.java | 118 ++++++++++++------ .../systemui/ScreenDecorationsTest.java | 39 +++--- 7 files changed, 213 insertions(+), 49 deletions(-) create mode 100644 packages/SystemUI/res/drawable/rounded_corner_bottom.xml create mode 100644 packages/SystemUI/res/drawable/rounded_corner_top.xml create mode 100644 packages/SystemUI/res/layout/rounded_corners_bottom.xml create mode 100644 packages/SystemUI/res/layout/rounded_corners_top.xml diff --git a/packages/SystemUI/res/drawable/rounded_corner_bottom.xml b/packages/SystemUI/res/drawable/rounded_corner_bottom.xml new file mode 100644 index 0000000000000..ef1a82f9798c7 --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_corner_bottom.xml @@ -0,0 +1,16 @@ + + + diff --git a/packages/SystemUI/res/drawable/rounded_corner_top.xml b/packages/SystemUI/res/drawable/rounded_corner_top.xml new file mode 100644 index 0000000000000..79348928a7a8f --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_corner_top.xml @@ -0,0 +1,16 @@ + + + diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml index 1849068d91b83..db892d78c5563 100644 --- a/packages/SystemUI/res/layout/rounded_corners.xml +++ b/packages/SystemUI/res/layout/rounded_corners.xml @@ -16,6 +16,7 @@ --> + + + + + diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml new file mode 100644 index 0000000000000..813c97d06f579 --- /dev/null +++ b/packages/SystemUI/res/layout/rounded_corners_top.xml @@ -0,0 +1,36 @@ + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 7861211e802da..6c06553a84a6c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -48,10 +48,11 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.graphics.drawable.VectorDrawable; +import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.HandlerExecutor; @@ -117,12 +118,15 @@ public class ScreenDecorations extends SystemUI implements Tunable { private DisplayManager.DisplayListener mDisplayListener; private CameraAvailabilityListener mCameraListener; + //TODO: These are piecemeal being updated to Points for now to support non-square rounded + // corners. for now it is only supposed when reading the intrinsic size from the drawables with + // mIsRoundedCornerMultipleRadius is set @VisibleForTesting - protected int mRoundedDefault; + protected Point mRoundedDefault = new Point(0, 0); @VisibleForTesting - protected int mRoundedDefaultTop; + protected Point mRoundedDefaultTop = new Point(0, 0); @VisibleForTesting - protected int mRoundedDefaultBottom; + protected Point mRoundedDefaultBottom = new Point(0, 0); @VisibleForTesting protected View[] mOverlays; @Nullable @@ -375,8 +379,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { if (mOverlays[pos] != null) { return; } - mOverlays[pos] = LayoutInflater.from(mContext) - .inflate(R.layout.rounded_corners, null); + mOverlays[pos] = overlayForPosition(pos); mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this); ((ViewGroup) mOverlays[pos]).addView(mCutoutViews[pos]); @@ -405,6 +408,23 @@ public class ScreenDecorations extends SystemUI implements Tunable { new ValidatingPreDrawListener(mOverlays[pos])); } + /** + * Allow overrides for top/bottom positions + */ + private View overlayForPosition(@BoundsPosition int pos) { + switch (pos) { + case BOUNDS_POSITION_TOP: + return LayoutInflater.from(mContext) + .inflate(R.layout.rounded_corners_top, null); + case BOUNDS_POSITION_BOTTOM: + return LayoutInflater.from(mContext) + .inflate(R.layout.rounded_corners_bottom, null); + default: + return LayoutInflater.from(mContext) + .inflate(R.layout.rounded_corners, null); + } + } + private void updateView(@BoundsPosition int pos) { if (mOverlays == null || mOverlays[pos] == null) { return; @@ -590,27 +610,36 @@ public class ScreenDecorations extends SystemUI implements Tunable { } private void updateRoundedCornerRadii() { + // We should eventually move to just using the intrinsic size of the drawables since + // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not + // upgrading all of the configs to contain (width, height) pairs. Instead assume that a + // device configured using the single integer config value is okay with drawing the corners + // as a square final int newRoundedDefault = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.rounded_corner_radius); final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.rounded_corner_radius_top); final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.rounded_corner_radius_bottom); - final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault - || mRoundedDefaultBottom != newRoundedDefaultBottom - || mRoundedDefaultTop != newRoundedDefaultTop; - if (roundedCornersChanged) { + final boolean changed = mRoundedDefault.x != newRoundedDefault + || mRoundedDefaultTop.x != newRoundedDefault + || mRoundedDefaultBottom.x != newRoundedDefault; + + if (changed) { // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the - // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius + // (width, height) size of drawable/rounded.xml instead of rounded_corner_radius if (mIsRoundedCornerMultipleRadius) { - final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded); - mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight()); - mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault; + Drawable d = mContext.getDrawable(R.drawable.rounded); + mRoundedDefault.set(d.getIntrinsicWidth(), d.getIntrinsicHeight()); + d = mContext.getDrawable(R.drawable.rounded_corner_top); + mRoundedDefaultTop.set(d.getIntrinsicWidth(), d.getIntrinsicHeight()); + d = mContext.getDrawable(R.drawable.rounded_corner_bottom); + mRoundedDefaultBottom.set(d.getIntrinsicWidth(), d.getIntrinsicHeight()); } else { - mRoundedDefault = newRoundedDefault; - mRoundedDefaultTop = newRoundedDefaultTop; - mRoundedDefaultBottom = newRoundedDefaultBottom; + mRoundedDefault.set(newRoundedDefault, newRoundedDefault); + mRoundedDefaultTop.set(newRoundedDefaultTop, newRoundedDefaultTop); + mRoundedDefaultBottom.set(newRoundedDefaultBottom, newRoundedDefaultBottom); } onTuningChanged(SIZE, null); } @@ -625,7 +654,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { if (shouldShowRoundedCorner(pos)) { final int gravity = getRoundedCornerGravity(pos, id == R.id.left); ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; - rounded.setRotation(getRoundedCornerRotation(gravity)); + setRoundedCornerOrientation(rounded, gravity); rounded.setVisibility(View.VISIBLE); } } @@ -646,23 +675,38 @@ public class ScreenDecorations extends SystemUI implements Tunable { } } - private int getRoundedCornerRotation(int gravity) { + /** + * Configures the rounded corner drawable's view matrix based on the gravity. + * + * The gravity describes which corner to configure for, and the drawable we are rotating is + * assumed to be oriented for the top-left corner of the device regardless of the target corner. + * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or + * y-axis for the top-right and bottom-left corners. + */ + private void setRoundedCornerOrientation(View corner, int gravity) { + corner.setRotation(0); + corner.setScaleX(1); + corner.setScaleY(1); switch (gravity) { case Gravity.TOP | Gravity.LEFT: - return 0; + return; case Gravity.TOP | Gravity.RIGHT: - return 90; + corner.setScaleX(-1); // flip X axis + return; case Gravity.BOTTOM | Gravity.LEFT: - return 270; + corner.setScaleY(-1); // flip Y axis + return; case Gravity.BOTTOM | Gravity.RIGHT: - return 180; + corner.setRotation(180); + return; default: throw new IllegalArgumentException("Unsupported gravity: " + gravity); } } - private boolean hasRoundedCorners() { - return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0 + return mRoundedDefault.x > 0 + || mRoundedDefaultBottom.x > 0 + || mRoundedDefaultTop.x > 0 || mIsRoundedCornerMultipleRadius; } @@ -712,12 +756,13 @@ public class ScreenDecorations extends SystemUI implements Tunable { mHandler.post(() -> { if (mOverlays == null) return; if (SIZE.equals(key)) { - int size = mRoundedDefault; - int sizeTop = mRoundedDefaultTop; - int sizeBottom = mRoundedDefaultBottom; + Point size = mRoundedDefault; + Point sizeTop = mRoundedDefaultTop; + Point sizeBottom = mRoundedDefaultBottom; if (newValue != null) { try { - size = (int) (Integer.parseInt(newValue) * mDensity); + int s = (int) (Integer.parseInt(newValue) * mDensity); + size = new Point(s, s); } catch (Exception e) { } } @@ -726,14 +771,17 @@ public class ScreenDecorations extends SystemUI implements Tunable { }); } - private void updateRoundedCornerSize(int sizeDefault, int sizeTop, int sizeBottom) { + private void updateRoundedCornerSize( + Point sizeDefault, + Point sizeTop, + Point sizeBottom) { if (mOverlays == null) { return; } - if (sizeTop == 0) { + if (sizeTop.x == 0) { sizeTop = sizeDefault; } - if (sizeBottom == 0) { + if (sizeBottom.x == 0) { sizeBottom = sizeDefault; } @@ -760,10 +808,10 @@ public class ScreenDecorations extends SystemUI implements Tunable { } @VisibleForTesting - protected void setSize(View view, int pixelSize) { + protected void setSize(View view, Point pixelSize) { LayoutParams params = view.getLayoutParams(); - params.width = pixelSize; - params.height = pixelSize; + params.width = pixelSize.x; + params.height = pixelSize.y; view.setLayoutParams(params); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index b9ddff3a59ea8..d107f646bb6b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -44,6 +44,7 @@ import static org.mockito.Mockito.when; import android.content.res.Configuration; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.VectorDrawable; import android.hardware.display.DisplayManager; @@ -193,6 +194,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { @Test public void testRoundingRadius_NoCutout() { final int testRadius = 1; + final Point testRadiusPoint = new Point(1, 1); mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false); mContext.getOrCreateTestableResources().addOverride( @@ -209,9 +211,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // Size of corner view should same as rounded_corner_radius{_top|_bottom} - assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadius); - assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadius); - assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadius); + assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadiusPoint); + assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadiusPoint); + assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadiusPoint); } @Test @@ -237,14 +239,18 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()).setSize(leftRoundedCorner, testTopRadius); - verify(mScreenDecorations, atLeastOnce()).setSize(rightRoundedCorner, testTopRadius); + verify(mScreenDecorations, atLeastOnce()) + .setSize(leftRoundedCorner, new Point(testTopRadius, testTopRadius)); + verify(mScreenDecorations, atLeastOnce()) + .setSize(rightRoundedCorner, new Point(testTopRadius, testTopRadius)); leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()).setSize(leftRoundedCorner, testBottomRadius); - verify(mScreenDecorations, atLeastOnce()).setSize(rightRoundedCorner, testBottomRadius); + verify(mScreenDecorations, atLeastOnce()) + .setSize(leftRoundedCorner, new Point(testBottomRadius, testBottomRadius)); + verify(mScreenDecorations, atLeastOnce()) + .setSize(rightRoundedCorner, new Point(testBottomRadius, testBottomRadius)); } @Test @@ -276,20 +282,24 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()).setSize(leftRoundedCorner, testTopRadius); - verify(mScreenDecorations, atLeastOnce()).setSize(rightRoundedCorner, testBottomRadius); + verify(mScreenDecorations, atLeastOnce()) + .setSize(leftRoundedCorner, new Point(testTopRadius, testTopRadius)); + verify(mScreenDecorations, atLeastOnce()) + .setSize(rightRoundedCorner, new Point(testBottomRadius, testBottomRadius)); leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()).setSize(leftRoundedCorner, testTopRadius); - verify(mScreenDecorations, atLeastOnce()).setSize(rightRoundedCorner, testBottomRadius); + verify(mScreenDecorations, atLeastOnce()) + .setSize(leftRoundedCorner, new Point(testTopRadius, testTopRadius)); + verify(mScreenDecorations, atLeastOnce()) + .setSize(rightRoundedCorner, new Point(testBottomRadius, testBottomRadius)); } @Test public void testRoundingMultipleRadius_NoCutout() { final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded); - final int multipleRadiusSize = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight()); + final Point multipleRadiusSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight()); mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false); @@ -600,14 +610,15 @@ public class ScreenDecorationsTest extends SysuiTestCase { .addOverride(R.bool.config_roundedCornerMultipleRadius, false); mScreenDecorations.start(); - assertEquals(mScreenDecorations.mRoundedDefault, 20); + assertEquals(mScreenDecorations.mRoundedDefault, new Point(20, 20)); mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.dimen.rounded_corner_radius, 5); mScreenDecorations.onConfigurationChanged(null); - assertEquals(mScreenDecorations.mRoundedDefault, 5); + assertEquals(mScreenDecorations.mRoundedDefault, new Point(5, 5)); } + @Test public void testBoundingRectsToRegion() throws Exception { Rect rect = new Rect(1, 2, 3, 4);