diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 649bfce5ae955..43d91a24bd3f8 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -200,49 +200,45 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } }; - private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener = + @VisibleForTesting + PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener = new PrivacyDotViewController.ShowingListener() { @Override public void onPrivacyDotShown(@Nullable View v) { - // We don't need to control the window visibility when the hwc doesn't support screen - // decoration since the overlay windows are always visible in this case. - if (mHwcScreenDecorationSupport == null || v == null) { - return; - } - mExecutor.execute(() -> { - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - if (overlayView.findViewById(v.getId()) != null) { - overlayView.setVisibility(View.VISIBLE); - } - } - }); + setOverlayWindowVisibilityIfViewExist(v, View.VISIBLE); } @Override public void onPrivacyDotHidden(@Nullable View v) { - // We don't need to control the window visibility when the hwc doesn't support screen - // decoration since the overlay windows are always visible in this case. - if (mHwcScreenDecorationSupport == null || v == null) { - return; - } - mExecutor.execute(() -> { - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - if (overlayView.findViewById(v.getId()) != null) { - overlayView.setVisibility(View.INVISIBLE); - } - } - }); + setOverlayWindowVisibilityIfViewExist(v, View.INVISIBLE); } }; + @VisibleForTesting + protected void setOverlayWindowVisibilityIfViewExist(@Nullable View view, + @View.Visibility int visibility) { + if (view == null) { + return; + } + mExecutor.execute(() -> { + // We don't need to control the window visibility if rounded corners or cutout is drawn + // on sw layer since the overlay windows are always visible in this case. + if (mOverlays == null || !isOnlyPrivacyDotInSwLayer()) { + return; + } + + for (final OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + if (overlay.getView(view.getId()) != null) { + overlay.getRootView().setVisibility(visibility); + return; + } + } + }); + } + private static boolean eq(DisplayDecorationSupport a, DisplayDecorationSupport b) { if (a == null) return (b == null); if (b == null) return false; @@ -268,7 +264,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController = dotViewController; mThreadFactory = threadFactory; mDotFactory = dotFactory; - dotViewController.setShowingListener(mPrivacyDotShowingListener); } @Override @@ -425,18 +420,24 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab removeHwcOverlay(); } final DisplayCutout cutout = getCutout(); + final boolean isOnlyPrivacyDotInSwLayer = isOnlyPrivacyDotInSwLayer(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout) - || shouldShowPrivacyDot(i, cutout)) { + if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout) + || shouldShowSwLayerPrivacyDot(i, cutout)) { Pair, List> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); - createOverlay(i, cutout, pair.getFirst()); + createOverlay(i, cutout, pair.getFirst(), isOnlyPrivacyDotInSwLayer); } else { removeOverlay(i); } } + if (isOnlyPrivacyDotInSwLayer) { + mDotViewController.setShowingListener(mPrivacyDotShowingListener); + } else { + mDotViewController.setShowingListener(null); + } final View tl, tr, bl, br; if ((tl = getOverlayView(R.id.privacy_dot_top_left_container)) != null && (tr = getOverlayView(R.id.privacy_dot_top_right_container)) != null @@ -530,19 +531,51 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mOverlays[pos] = null; } + @View.Visibility + private int getWindowVisibility(@NonNull OverlayWindow overlay, + boolean isOnlyPrivacyDotInSwLayer) { + if (!isOnlyPrivacyDotInSwLayer) { + // Multiple views inside overlay, no need to optimize + return View.VISIBLE; + } + + int[] ids = { + R.id.privacy_dot_top_left_container, + R.id.privacy_dot_top_right_container, + R.id.privacy_dot_bottom_left_container, + R.id.privacy_dot_bottom_right_container + }; + for (int id: ids) { + final View view = overlay.getView(id); + if (view != null && view.getVisibility() == View.VISIBLE) { + // Only privacy dot in sw layers, overlay shall be VISIBLE if one of privacy dot + // views inside this overlay is VISIBLE + return View.VISIBLE; + } + } + // Only privacy dot in sw layers, overlay shall be INVISIBLE like default if no privacy dot + // view inside this overlay is VISIBLE. + return View.INVISIBLE; + } + private void createOverlay( @BoundsPosition int pos, @Nullable DisplayCutout cutout, - @NonNull List decorProviders) { + @NonNull List decorProviders, + boolean isOnlyPrivacyDotInSwLayer) { if (mOverlays == null) { mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH]; } if (mOverlays[pos] != null) { + // When mOverlay[pos] is not null and only privacy dot in sw layer, use privacy dot + // view's visibility + mOverlays[pos].getRootView().setVisibility( + getWindowVisibility(mOverlays[pos], isOnlyPrivacyDotInSwLayer)); return; } - mOverlays[pos] = overlayForPosition(pos, decorProviders); + mOverlays[pos] = overlayForPosition(pos, decorProviders, isOnlyPrivacyDotInSwLayer); final ViewGroup overlayView = mOverlays[pos].getRootView(); overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); overlayView.setAlpha(0); @@ -612,18 +645,18 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab */ private OverlayWindow overlayForPosition( @BoundsPosition int pos, - @NonNull List decorProviders) { + @NonNull List decorProviders, + boolean isOnlyPrivacyDotInSwLayer) { final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); decorProviders.forEach(provider -> { removeOverlayView(provider.getViewId()); currentOverlay.addDecorProvider(provider, mRotation); - // If the hwc supports screen decoration and privacy dot is enabled, it means there will - // be only privacy dot in mOverlay. So set the initial visibility of mOverlays to - // INVISIBLE and will only set it to VISIBLE when the privacy dot is showing. - if (mHwcScreenDecorationSupport != null) { - currentOverlay.getRootView().setVisibility(View.INVISIBLE); - } }); + // When only privacy dot in mOverlay, set the initial visibility of mOverlays to + // INVISIBLE and set it to VISIBLE when the privacy dot is showing. + if (isOnlyPrivacyDotInSwLayer) { + currentOverlay.getRootView().setVisibility(View.INVISIBLE); + } return currentOverlay; } @@ -842,6 +875,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab pw.println("ScreenDecorations state:"); pw.println(" DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS); pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled()); + pw.println(" isOnlyPrivacyDotInSwLayer:" + isOnlyPrivacyDotInSwLayer()); pw.println(" mPendingRotationChange:" + mPendingRotationChange); if (mHwcScreenDecorationSupport != null) { pw.println(" mHwcScreenDecorationSupport:"); @@ -923,7 +957,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } rounded.setVisibility(View.GONE); - if (shouldShowRoundedCorner(pos, cutout)) { + if (shouldShowSwLayerRoundedCorner(pos, cutout)) { final int gravity = getRoundedCornerGravity(pos, id == R.id.left); ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; setRoundedCornerOrientation(rounded, gravity); @@ -997,23 +1031,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } - private boolean shouldShowRoundedCorner(@BoundsPosition int pos, + private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout) && mHwcScreenDecorationSupport == null; } - private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { + private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos, + @Nullable DisplayCutout cutout) { return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout); } - private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { + private boolean shouldShowSwLayerCutout(@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() && mHwcScreenDecorationSupport == null); } + private boolean isOnlyPrivacyDotInSwLayer() { + return isPrivacyDotEnabled() + && (mHwcScreenDecorationSupport != null + || (!hasRoundedCorners() && !shouldDrawCutout()) + ); + } + private boolean shouldDrawCutout() { return shouldDrawCutout(mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 140142394c248..4f27fb4a9661e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -134,7 +134,7 @@ class PrivacyDotViewController @Inject constructor( uiExecutor = e } - fun setShowingListener(l: ShowingListener) { + fun setShowingListener(l: ShowingListener?) { showingListener = l } @@ -573,7 +573,7 @@ class PrivacyDotViewController @Inject constructor( } } - public interface ShowingListener { + interface ShowingListener { fun onPrivacyDotShown(v: View?) fun onPrivacyDotHidden(v: View?) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index bcccbc7fcda81..50bd9b0947610 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -30,6 +30,7 @@ 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.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; @@ -127,6 +128,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private CornerDecorProvider mPrivacyDotBottomRightDecorProvider; @Mock private Display.Mode mDisplayMode; + private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener; @Before public void setup() { @@ -195,10 +197,24 @@ public class ScreenDecorationsTest extends SysuiTestCase { super.onTuningChanged(key, newValue); mExecutor.runAllReady(); } + + @Override + protected void setOverlayWindowVisibilityIfViewExist(@Nullable View view, + @View.Visibility int visibility) { + super.setOverlayWindowVisibilityIfViewExist(view, visibility); + mExecutor.runAllReady(); + } }); reset(mTunerService); - } + try { + mPrivacyDotShowingListener = mScreenDecorations.mPrivacyDotShowingListener.getClass() + .getDeclaredConstructor(ScreenDecorations.class) + .newInstance(mScreenDecorations); + } catch (Exception e) { + fail(e.getMessage()); + } + } private void verifyRoundedCornerViewsVisibility( @DisplayCutout.BoundsPosition final int overlayId, @@ -277,43 +293,59 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyBottomDotViewsVisibility(visibility); } - private void verifyOverlaysExistAndAdded(final boolean left, final boolean top, - final boolean right, final boolean bottom) { + private void verifyOverlaysExistAndAdded(boolean left, boolean top, boolean right, + boolean bottom, @Nullable Integer visibilityIfExist) { if (left || top || right || bottom) { assertNotNull(mScreenDecorations.mOverlays); } else { - verify(mWindowManager, never()).addView(any(), any()); + assertNull(mScreenDecorations.mOverlays); return; } if (left) { - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]); + final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]; + assertNotNull(overlay); verify(mWindowManager, times(1)).addView( - eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()), any()); + eq(overlay.getRootView()), any()); + if (visibilityIfExist != null) { + assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility()); + } } else { assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]); } if (top) { - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]); + final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]; + assertNotNull(overlay); verify(mWindowManager, times(1)).addView( - eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()), any()); + eq(overlay.getRootView()), any()); + if (visibilityIfExist != null) { + assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility()); + } } else { assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]); } if (right) { - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]); + final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]; + assertNotNull(overlay); verify(mWindowManager, times(1)).addView( - eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()), any()); + eq(overlay.getRootView()), any()); + if (visibilityIfExist != null) { + assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility()); + } } else { assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]); } if (bottom) { - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]); + final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]; + assertNotNull(overlay); verify(mWindowManager, times(1)).addView( - eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()), any()); + eq(overlay.getRootView()), any()); + if (visibilityIfExist != null) { + assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility()); + } } else { assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]); } @@ -330,7 +362,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // No views added. - verifyOverlaysExistAndAdded(false, false, false, false); + verifyOverlaysExistAndAdded(false, false, false, false, null); // No Tuners tuned. verify(mTunerService, never()).addTunable(any(), any()); // No dot controller init @@ -348,9 +380,12 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); - // Top and bottom windows are created for privacy dot. + // Top and bottom windows are created with INVISIBLE because of privacy dot only // Left and right window should be null. - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener( + mScreenDecorations.mPrivacyDotShowingListener); // Rounded corner views shall not exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); @@ -379,7 +414,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top and bottom windows are created for rounded corners. // Left and right window should be null. - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -407,7 +442,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top and bottom windows are created for rounded corners. // Left and right window should be null. - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -522,7 +559,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // Top and bottom windows are created for rounded corners. // Left and right window should be null. - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -556,7 +593,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // Top and bottom windows are created for rounded corners. // Left and right window should be null. - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -591,7 +630,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // Top window is created for top cutout. // Bottom, left, or right window should be null. - verifyOverlaysExistAndAdded(false, true, false, false); + verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE); // Privacy dots shall not exist because of no privacy verifyDotViewsNullable(true); @@ -615,7 +654,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Top rounded corner views shall exist because of cutout // but be gone because of no rounded corner @@ -646,7 +687,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // Left window is created for left cutout. // Bottom, top, or right window should be null. - verifyOverlaysExistAndAdded(true, false, false, false); + verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE); // Left rounded corner views shall exist because of cutout // but be gone because of no rounded corner @@ -674,7 +715,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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); + verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -699,7 +742,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -727,7 +770,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); @@ -756,7 +801,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Left window is created for rounded corner and left cutout. // Right window is created for rounded corner. // Top, or bottom window should be null. - verifyOverlaysExistAndAdded(true, false, true, false); + verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE); } @Test @@ -774,7 +819,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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); + verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); } @Test @@ -793,7 +840,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Bottom window is created for rounded corner. // Left window is created for left cutout. // Right window should be null. - verifyOverlaysExistAndAdded(true, true, false, true); + verifyOverlaysExistAndAdded(true, true, false, true, View.VISIBLE); } @Test @@ -812,7 +859,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Bottom window is created for rounded corner. // Left window is created for left cutout. // Right window should be null. - verifyOverlaysExistAndAdded(true, true, false, true); + verifyOverlaysExistAndAdded(true, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); } @Test @@ -827,7 +876,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - verifyOverlaysExistAndAdded(false, true, false, false); + verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE); // Switch to long edge cutout(left). final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null}; @@ -835,7 +884,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verifyOverlaysExistAndAdded(true, false, false, false); + verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE); } @Test @@ -850,7 +899,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - verifyOverlaysExistAndAdded(false, true, false, true); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); // Switch to long edge cutout(left). final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null}; @@ -858,7 +909,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verifyOverlaysExistAndAdded(true, false, true, false); + verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE); + verify(mDotViewController, times(2)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(2)).setShowingListener(null); // Verify each privacy dot id appears only once mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { @@ -889,7 +942,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - assertNull(mScreenDecorations.mOverlays); + verifyOverlaysExistAndAdded(false, false, false, false, null); when(mContext.getResources().getBoolean( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout)) @@ -897,7 +950,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.onConfigurationChanged(new Configuration()); // Only top windows should be added. - verifyOverlaysExistAndAdded(false, true, false, false); + verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE); } @Test @@ -912,23 +965,27 @@ public class ScreenDecorationsTest extends SysuiTestCase { .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); + // Both top and bottom windows should be added with INVISIBLE because of only privacy dot, + // but rounded corners visibility shall be gone because of no rounding. + verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener( + mScreenDecorations.mPrivacyDotShowingListener); 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); + // Both top and bottom windows should be added with VISIBLE because of privacy dot and + // cutout, but rounded corners visibility shall be gone because of no rounding. + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verify(mDotViewController, times(2)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener(null); } @Test @@ -1043,9 +1100,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // should only inflate mOverlays when the hwc doesn't support screen decoration assertNull(mScreenDecorations.mScreenDecorHwcWindow); - assertNotNull(mScreenDecorations.mOverlays); - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]); - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; @@ -1056,7 +1111,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // should only inflate hwc layer when the hwc supports screen decoration assertNotNull(mScreenDecorations.mScreenDecorHwcWindow); - assertNull(mScreenDecorations.mOverlays); + verifyOverlaysExistAndAdded(false, false, false, false, null); } @Test @@ -1076,7 +1131,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // should only inflate hwc layer when the hwc supports screen decoration assertNotNull(mScreenDecorations.mScreenDecorHwcWindow); - assertNull(mScreenDecorations.mOverlays); + verifyOverlaysExistAndAdded(false, false, false, false, null); doReturn(null).when(mDisplay).getDisplayDecorationSupport(); // Trigger the support hwc screen decoration change by changing the display unique id @@ -1085,9 +1140,66 @@ public class ScreenDecorationsTest extends SysuiTestCase { // should only inflate mOverlays when the hwc doesn't support screen decoration assertNull(mScreenDecorations.mScreenDecorHwcWindow); - assertNotNull(mScreenDecorations.mOverlays); - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]); - assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]); + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + } + + @Test + public void testPrivacyDotShowingListenerWorkWellWithNullParameter() { + mPrivacyDotShowingListener.onPrivacyDotShown(null); + mPrivacyDotShowingListener.onPrivacyDotHidden(null); + } + + @Test + public void testAutoShowHideOverlayWindowWhenSupportHwcLayer() { + setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + true /* fillCutout */, true /* privacyDot */); + final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); + decorationSupport.format = PixelFormat.R_8; + doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); + + // 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(); + // Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer + verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); + + // Make sure view found and window visibility changed as well + final View view = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() + .findViewById(R.id.privacy_dot_bottom_right_container); + mPrivacyDotShowingListener.onPrivacyDotShown(view); + assertEquals(View.VISIBLE, + mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility()); + mPrivacyDotShowingListener.onPrivacyDotHidden(view); + assertEquals(View.INVISIBLE, + mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility()); + } + + @Test + public void testAutoShowHideOverlayWindowWhenNoRoundedAndNoCutout() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + false /* fillCutout */, true /* privacyDot */); + + // no cutout + doReturn(null).when(mScreenDecorations).getCutout(); + + mScreenDecorations.start(); + // Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer + verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); + + // Make sure view found and window visibility changed as well + final View view = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() + .findViewById(R.id.privacy_dot_bottom_right_container); + mPrivacyDotShowingListener.onPrivacyDotShown(view); + assertEquals(View.VISIBLE, + mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility()); + mPrivacyDotShowingListener.onPrivacyDotHidden(view); + assertEquals(View.INVISIBLE, + mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility()); } @Test @@ -1108,7 +1220,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Should only inflate hwc layer. assertNotNull(mScreenDecorations.mScreenDecorHwcWindow); - assertNull(mScreenDecorations.mOverlays); + verifyOverlaysExistAndAdded(false, false, false, false, null); } @Test @@ -1128,13 +1240,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); assertNotNull(mScreenDecorations.mScreenDecorHwcWindow); - // mOverlays are inflated but the visibility should be GONE. - assertNotNull(mScreenDecorations.mOverlays); - final View topOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView(); - final View botOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView(); - assertEquals(topOverlay.getVisibility(), View.INVISIBLE); - assertEquals(botOverlay.getVisibility(), View.INVISIBLE); - + // mOverlays are inflated but the visibility should be INVISIBLE. + verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); + verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); + verify(mDotViewController, times(1)).setShowingListener( + mScreenDecorations.mPrivacyDotShowingListener); } @Test