Add privacy dot flag for SystemUI

Add privacy dot flag, it is used to clearly indicate related components
to keep the maring spaces, or add related overlay views for it.

And also prevent NPE issues at b/199060326 even the phone configs for
cutout or rounded conrner are incorrect.

Detail:
1. ScreenDecorations:
   a.Read privacy dot flag, and follow original
   shouldShowRoundedCorner() method logic to provide another
   shouldShowPrivacyDot() method. The existence of privacy dot container
   is determinated throguht the return value of shouldShowPrivacyDot().
   b. DisplayCutout::getCutout is a sync method, cache the return value
   as local variable.
2. KeyguardStatusBarView: update keyguard status bar left/right margin
as this config.
3. StatusBarContentInsetsProvider: update phone status bar left/right
margin as this config

Bug: 190922585
Bug: 199060326
Bug: 199463850
Test: atest com.android.systemui.ScreenDecorationsTest
Change-Id: If1aa97b877ba374d6fa47615591bb91a4ebab421
This commit is contained in:
Milton Wu
2021-09-11 12:24:49 +00:00
parent 48fc814a73
commit 0f3e6b7ac1
5 changed files with 540 additions and 146 deletions

View File

@@ -711,4 +711,8 @@
<item>@drawable/rounded_corner_bottom</item>
<item>@drawable/rounded_corner_bottom_secondary</item>
</array>
<!-- Flag to enable privacy dot views, it shall be true for normal case -->
<bool name="config_enablePrivacyDot">true</bool>
</resources>

View File

@@ -160,6 +160,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
private boolean mIsPrivacyDotEnabled;
private int mStatusBarHeightPortrait;
private int mStatusBarHeightLandscape;
private Drawable mRoundedCornerDrawable;
@@ -253,6 +254,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mRotation = mContext.getDisplay().getRotation();
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
updateRoundedCornerDrawable();
@@ -312,24 +314,24 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
private void setupDecorations() {
if (hasRoundedCorners() || shouldDrawCutout()) {
if (hasRoundedCorners() || shouldDrawCutout() || mIsPrivacyDotEnabled) {
updateStatusBarHeight();
final DisplayCutout cutout = getCutout();
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
int rotatedPos;
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
rotatedPos = getBoundPositionFromRotation(i, mRotation);
if ((bounds != null && !bounds[rotatedPos].isEmpty())
|| shouldShowRoundedCorner(i)) {
createOverlay(i);
if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
|| shouldShowPrivacyDot(i, cutout)) {
createOverlay(i, cutout);
} else {
removeOverlay(i);
}
}
// Overlays have been created, send the dots to the controller
//TODO: need a better way to do this
mDotViewController.initialize(
mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
if (mIsPrivacyDotEnabled) {
// Overlays have been created, send the dots to the controller
//TODO: need a better way to do this
mDotViewController.initialize(
mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
}
} else {
removeAllOverlays();
}
@@ -416,7 +418,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mOverlays[pos] = null;
}
private void createOverlay(@BoundsPosition int pos) {
private void createOverlay(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
if (mOverlays == null) {
mOverlays = new View[BOUNDS_POSITION_LENGTH];
}
@@ -437,7 +439,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mOverlays[pos].setAlpha(0);
mOverlays[pos].setForceDarkAllowed(false);
updateView(pos);
updateView(pos, cutout);
mWindowManager.addView(mOverlays[pos], getWindowLayoutParams(pos));
@@ -461,34 +463,19 @@ public class ScreenDecorations extends SystemUI implements Tunable {
* Allow overrides for top/bottom positions
*/
private View overlayForPosition(@BoundsPosition int pos) {
switch (pos) {
case BOUNDS_POSITION_TOP:
case BOUNDS_POSITION_LEFT:
View top = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners_top, null);
mTopLeftDot = top.findViewById(R.id.privacy_dot_left_container);
mTopRightDot = top.findViewById(R.id.privacy_dot_right_container);
return top;
case BOUNDS_POSITION_BOTTOM:
case BOUNDS_POSITION_RIGHT:
View bottom = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners_bottom, null);
mBottomLeftDot = bottom.findViewById(R.id.privacy_dot_left_container);
mBottomRightDot = bottom.findViewById(R.id.privacy_dot_right_container);
return bottom;
default:
throw new IllegalArgumentException("Unknown bounds position");
}
final int layoutId = (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP)
? R.layout.rounded_corners_top : R.layout.rounded_corners_bottom;
return LayoutInflater.from(mContext).inflate(layoutId, null);
}
private void updateView(@BoundsPosition int pos) {
private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
if (mOverlays == null || mOverlays[pos] == null) {
return;
}
// update rounded corner view rotation
updateRoundedCornerView(pos, R.id.left);
updateRoundedCornerView(pos, R.id.right);
updateRoundedCornerView(pos, R.id.left, cutout);
updateRoundedCornerView(pos, R.id.right, cutout);
updateRoundedCornerSize(mRoundedDefault, mRoundedDefaultTop, mRoundedDefaultBottom);
updateRoundedCornerImageView();
@@ -496,6 +483,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
if (mCutoutViews != null && mCutoutViews[pos] != null) {
mCutoutViews[pos].setRotation(mRotation);
}
updatePrivacyDotView(pos, cutout);
}
@VisibleForTesting
@@ -671,11 +660,12 @@ public class ScreenDecorations extends SystemUI implements Tunable {
if (mOverlays != null) {
updateLayoutParams();
final DisplayCutout cutout = getCutout();
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (mOverlays[i] == null) {
continue;
}
updateView(i);
updateView(i, cutout);
}
}
}
@@ -807,13 +797,14 @@ public class ScreenDecorations extends SystemUI implements Tunable {
return drawable;
}
private void updateRoundedCornerView(@BoundsPosition int pos, int id) {
private void updateRoundedCornerView(@BoundsPosition int pos, int id,
@Nullable DisplayCutout cutout) {
final View rounded = mOverlays[pos].findViewById(id);
if (rounded == null) {
return;
}
rounded.setVisibility(View.GONE);
if (shouldShowRoundedCorner(pos)) {
if (shouldShowRoundedCorner(pos, cutout)) {
final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
setRoundedCornerOrientation(rounded, gravity);
@@ -821,6 +812,26 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
}
private void updatePrivacyDotView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
final ViewGroup viewGroup = (ViewGroup) mOverlays[pos];
final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
if (shouldShowPrivacyDot(pos, cutout)) {
// TODO (b/201481944) Privacy Dots pos and var are wrong with long side cutout enable
if (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP) {
mTopLeftDot = left;
mTopRightDot = right;
} else {
mBottomLeftDot = left;
mBottomRightDot = right;
}
} else {
viewGroup.removeView(left);
viewGroup.removeView(right);
}
}
private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
switch (rotatedPos) {
@@ -872,12 +883,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|| mIsRoundedCornerMultipleRadius;
}
private boolean shouldShowRoundedCorner(@BoundsPosition int pos) {
if (!hasRoundedCorners()) {
return false;
}
DisplayCutout cutout = getCutout();
private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
@Nullable DisplayCutout cutout) {
// for cutout is null or cutout with only waterfall.
final boolean emptyBoundsOrWaterfall = cutout == null || cutout.isBoundsEmpty();
// Shows rounded corner on left and right overlays only when there is no top or bottom
@@ -892,6 +899,21 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
}
private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
@Nullable DisplayCutout cutout) {
return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout);
}
private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
return mIsPrivacyDotEnabled && isDefaultShownOverlayPos(pos, cutout);
}
private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
return (bounds != null && !bounds[rotatedPos].isEmpty());
}
private boolean shouldDrawCutout() {
return shouldDrawCutout(mContext);
}

View File

@@ -72,6 +72,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
private boolean mKeyguardUserSwitcherEnabled;
private final UserManager mUserManager;
private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
private int mStatusBarPaddingEnd;
private int mMinDotWidth;
@@ -112,7 +113,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
mCutoutSpace = findViewById(R.id.cutout_space_view);
mStatusIconArea = findViewById(R.id.status_icon_area);
mStatusIconContainer = findViewById(R.id.statusIcons);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
}
@@ -270,9 +271,10 @@ public class KeyguardStatusBarView extends RelativeLayout {
mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
// consider privacy dot space
final int minLeft = isLayoutRtl() ? Math.max(mMinDotWidth, mPadding.first) : mPadding.first;
final int minRight = isLayoutRtl() ? mPadding.second :
Math.max(mMinDotWidth, mPadding.second);
final int minLeft = (isLayoutRtl() && mIsPrivacyDotEnabled)
? Math.max(mMinDotWidth, mPadding.first) : mPadding.first;
final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
? Math.max(mMinDotWidth, mPadding.second) : mPadding.second;
setPadding(minLeft, waterfallTop, minRight, 0);
}

View File

@@ -70,6 +70,9 @@ class StatusBarContentInsetsProvider @Inject constructor(
// (e.g. network displays)
private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
context.resources.getBoolean(R.bool.config_enablePrivacyDot)
}
init {
configurationController.addCallback(this)
@@ -152,8 +155,9 @@ class StatusBarContentInsetsProvider @Inject constructor(
val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
val roundedCornerPadding = rotatedResources
.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
val minDotWidth = rotatedResources
.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
val minDotWidth = if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
else 0
val minLeft: Int
val minRight: Int

View File

@@ -28,11 +28,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -151,27 +153,155 @@ public class ScreenDecorationsTest extends SysuiTestCase {
reset(mTunerService);
}
private void verifyRoundedCornerViewsVisibility(
@DisplayCutout.BoundsPosition final int overlayId,
@View.Visibility final int visibility) {
final View overlay = mScreenDecorations.mOverlays[overlayId];
final View left = overlay.findViewById(R.id.left);
final View right = overlay.findViewById(R.id.right);
assertNotNull(left);
assertNotNull(right);
assertThat(left.getVisibility()).isEqualTo(visibility);
assertThat(right.getVisibility()).isEqualTo(visibility);
}
private void verifyTopDotViewsNullable(final boolean isAssertNull) {
if (isAssertNull) {
assertNull(mScreenDecorations.mTopLeftDot);
assertNull(mScreenDecorations.mTopRightDot);
} else {
assertNotNull(mScreenDecorations.mTopLeftDot);
assertNotNull(mScreenDecorations.mTopRightDot);
}
}
private void verifyBottomDotViewsNullable(final boolean isAssertNull) {
if (isAssertNull) {
assertNull(mScreenDecorations.mBottomLeftDot);
assertNull(mScreenDecorations.mBottomRightDot);
} else {
assertNotNull(mScreenDecorations.mBottomLeftDot);
assertNotNull(mScreenDecorations.mBottomRightDot);
}
}
private void verifyDotViewsNullable(final boolean isAssertNull) {
verifyTopDotViewsNullable(isAssertNull);
verifyBottomDotViewsNullable(isAssertNull);
}
private void verifyTopDotViewsVisibility(@View.Visibility final int visibility) {
verifyTopDotViewsNullable(false);
assertThat(mScreenDecorations.mTopLeftDot.getVisibility()).isEqualTo(visibility);
assertThat(mScreenDecorations.mTopRightDot.getVisibility()).isEqualTo(visibility);
}
private void verifyBottomDotViewsVisibility(@View.Visibility final int visibility) {
verifyBottomDotViewsNullable(false);
assertThat(mScreenDecorations.mBottomLeftDot.getVisibility()).isEqualTo(visibility);
assertThat(mScreenDecorations.mBottomRightDot.getVisibility()).isEqualTo(visibility);
}
private void verifyDotViewsVisibility(@View.Visibility final int visibility) {
verifyTopDotViewsVisibility(visibility);
verifyBottomDotViewsVisibility(visibility);
}
private void verifyOverlaysExistAndAdded(final boolean left, final boolean top,
final boolean right, final boolean bottom) {
if (left || top || right || bottom) {
assertNotNull(mScreenDecorations.mOverlays);
} else {
verify(mWindowManager, never()).addView(any(), any());
return;
}
if (left) {
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
}
if (top) {
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
}
if (right) {
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
}
if (bottom) {
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
}
}
@Test
public void testNoRounding_NoCutout() {
public void testNoRounding_NoCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// No views added.
verify(mWindowManager, never()).addView(any(), any());
verifyOverlaysExistAndAdded(false, false, false, false);
// No Tuners tuned.
verify(mTunerService, never()).addTunable(any(), any());
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_NoCutout() {
public void testNoRounding_NoCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top and bottom windows are created for privacy dot.
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall not exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_NoCutout_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -179,17 +309,49 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
// Left and right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Privacy dots shall not exist
verifyDotViewsNullable(true);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_NoCutout_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
@@ -197,7 +359,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
final Point testRadiusPoint = new Point(1, 1);
setupResources(1 /* radius */, 1 /* radiusTop */, 1 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -213,7 +375,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
final int testTopRadius = 1;
final int testBottomRadius = 5;
setupResources(testTopRadius, testTopRadius, testBottomRadius, 0 /* roundedPadding */,
false /* multipleRadius */, false /* fillCutout */);
false /* multipleRadius */, false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -242,7 +404,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
final int testTopRadius = 1;
final int testBottomRadius = 5;
setupResources(testTopRadius, testTopRadius, testBottomRadius, 0 /* roundedPadding */,
false /* multipleRadius */, false /* fillCutout */);
false /* multipleRadius */, false /* fillCutout */, true /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -278,29 +440,32 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
public void testRoundingMultipleRadius_NoCutout() {
public void testRoundingMultipleRadius_NoCutout_NoPrivacyDot() {
final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
final Point multipleRadiusSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight());
setupResources(9999 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
9999 /* roundedPadding */, true /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
// Left and right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Privacy dots shall not exist
verifyDotViewsNullable(true);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
// Size of corner view should exactly match max(width, height) of R.drawable.rounded
assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
@@ -309,10 +474,45 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
public void testNoRounding_CutoutShortEdge() {
public void testRoundingMultipleRadius_NoCutout_PrivacyDot() {
final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
final Point multipleRadiusSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight());
setupResources(9999 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
9999 /* roundedPadding */, true /* multipleRadius */,
false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
// Size of corner view should exactly match max(width, height) of R.drawable.rounded
assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
}
@Test
public void testNoRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -321,21 +521,53 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Top window is created for top cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
// Bottom window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
// Left window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
// Right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
// Bottom, left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, false);
// Privacy dots shall not exist because of no privacy
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testNoRounding_CutoutLongEdge() {
public void testNoRounding_CutoutShortEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, true /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top window is created for top cutout.
// Bottom window is created for privacy dot.
// Left or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Top rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
// Bottom rounded corner views shall exist because of privacy dot
// but be gone because of no rounded corner
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testNoRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */, false /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -344,21 +576,50 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Left window is created for left cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
// Bottom window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
// Top window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
// Right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
// Bottom, top, or right window should be null.
verifyOverlaysExistAndAdded(true, false, false, false);
// Left rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE);
// Top privacy dots shall not exist because of no privacy
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_CutoutShortEdge() {
public void testNoRounding_CutoutLongEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */, true /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Left window is created for left cutout.
// Right window is created for privacy.
// Bottom, or top window should be null.
verifyOverlaysExistAndAdded(true, false, true, false);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -367,22 +628,55 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
// Bottom window is created for rounded corner.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
// Left window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
// Right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
// Left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Top privacy dots shall not exist because of no privacy dot
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_CutoutLongEdge() {
public void testRounding_CutoutShortEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, true /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
// Bottom window is created for rounded corner.
// Left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
// Top privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */, false /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -391,22 +685,34 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Left window is created for rounded corner and left cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
// Right window is created for rounded corner.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
// Top window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
// Bottom window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
// Top, or bottom window should be null.
verifyOverlaysExistAndAdded(true, false, true, false);
}
@Test
public void testRounding_CutoutShortAndLongEdge() {
public void testRounding_CutoutLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, true /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Left window is created for rounded corner, left cutout, and privacy.
// Right window is created for rounded corner and privacy dot.
// Top, or bottom window should be null.
verifyOverlaysExistAndAdded(true, false, true, false);
}
@Test
public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */, false /* privacyDot */);
// top and left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
@@ -415,23 +721,36 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
// Bottom window is created for rounded corner.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
// Left window is created for left cutout.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
// Right window should be null.
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
verifyOverlaysExistAndAdded(true, true, false, true);
}
@Test
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout() {
public void testRounding_CutoutShortAndLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */, true /* privacyDot */);
// top and left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
// Bottom window is created for rounded corner.
// Left window is created for left cutout.
// Right window should be null.
verifyOverlaysExistAndAdded(true, true, false, true);
}
@Test
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
true /* fillCutout */);
true /* fillCutout */, false /* privacyDot */);
// Set to short edge cutout(top).
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -439,11 +758,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
verifyOverlaysExistAndAdded(false, true, false, false);
// Switch to long edge cutout(left).
final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -451,18 +766,37 @@ public class ScreenDecorationsTest extends SysuiTestCase {
.when(mScreenDecorations).getCutout();
mScreenDecorations.onConfigurationChanged(new Configuration());
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
verifyOverlaysExistAndAdded(true, false, false, false);
}
@Test
public void testDelayedCutout() {
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
true /* fillCutout */, true /* privacyDot */);
// Set to short edge cutout(top).
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, true, false, true);
// Switch to long edge cutout(left).
final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.onConfigurationChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, true, false);
}
@Test
public void testDelayedCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -478,11 +812,38 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.onConfigurationChanged(new Configuration());
// Only top windows should be added.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
verifyOverlaysExistAndAdded(false, true, false, false);
}
@Test
public void testDelayedCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */, true /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Both top and bottom windows should be added because of privacy dot,
// but their visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
when(mContext.getResources().getBoolean(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
.thenReturn(true);
mScreenDecorations.onConfigurationChanged(new Configuration());
assertNotNull(mScreenDecorations.mOverlays);
// Both top and bottom windows should be added because of privacy dot,
// but their visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
}
@Test
@@ -496,7 +857,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
public void testUpdateRoundedCorners() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(mScreenDecorations.mRoundedDefault, new Point(20, 20));
@@ -511,7 +872,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
public void testOnlyRoundedCornerRadiusTop() {
setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
@@ -523,7 +884,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
public void testOnlyRoundedCornerRadiusBottom() {
setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
false /* fillCutout */);
false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
@@ -531,7 +892,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
assertEquals(new Point(20, 20), mScreenDecorations.mRoundedDefaultBottom);
}
@Test
public void testBoundingRectsToRegion() throws Exception {
Rect rect = new Rect(1, 2, 3, 4);
@@ -588,7 +948,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding,
boolean multipleRadius, boolean fillCutout) {
boolean multipleRadius, boolean fillCutout, boolean privacyDot) {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_displayUniqueIdArray,
new String[]{});
@@ -625,6 +985,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
R.bool.config_roundedCornerMultipleRadius, multipleRadius);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
mContext.getOrCreateTestableResources().addOverride(
R.bool.config_enablePrivacyDot, privacyDot);
}
private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {