From 5dc67de14458ff5c6ffaab55f252432da036d082 Mon Sep 17 00:00:00 2001 From: kwaky Date: Thu, 28 May 2020 11:55:54 -0700 Subject: [PATCH] Add occlusion logic to OverlayViewGlobalStateController and CarKeyguardViewController Activity has a public API setShowWhenLocked, which, if set to true, allows it to be shown when the device is locked, thereby "occluding" the Keyguard. In the Phone OS implementation, when an occluding Activity is showing, Keyguard is temporarily dismissed and StatusBar expands. In the AAOS implementation where SystemUI components are mounted to the SystemUIOverlayWindow, OverlayViewGlobalStateController hides all views mounted to it unless they are configured to be shown even during occlusion. Test: Unit Tests + atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTopResumedStateTests#testTopPositionLaunchedOnTopOfLockScreen Bug: 156781505 Change-Id: I8320e97a575a990ba4301729c6b0e6c86d6ef7c5 --- .../keyguard/CarKeyguardViewController.java | 13 ++-- .../car/window/OverlayViewController.java | 7 ++ .../OverlayViewGlobalStateController.java | 56 ++++++++++++++ .../CarKeyguardViewControllerTest.java | 13 ++++ .../OverlayViewGlobalStateControllerTest.java | 75 +++++++++++++++++++ 5 files changed, 159 insertions(+), 5 deletions(-) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index ab61b443df976..2dad5f872e73e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -218,6 +218,14 @@ public class CarKeyguardViewController extends OverlayViewController implements } } + @Override + public void setOccluded(boolean occluded, boolean animate) { + getOverlayViewGlobalStateController().setOccluded(occluded); + if (!occluded) { + reset(/* hideBouncerWhenShowing= */ false); + } + } + @Override public void onCancelClicked() { if (mBouncer == null) return; @@ -314,11 +322,6 @@ public class CarKeyguardViewController extends OverlayViewController implements // no-op } - @Override - public void setOccluded(boolean occluded, boolean animate) { - // no-op - } - @Override public boolean shouldDisableWindowAnimationsForUnlock() { return false; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java index 30e26578bd733..3969f92c690aa 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java @@ -138,4 +138,11 @@ public class OverlayViewController { protected boolean shouldShowNavigationBar() { return false; } + + /** + * Returns {@code true} if this view should be hidden during the occluded state. + */ + protected boolean shouldShowWhenOccluded() { + return false; + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 70260b0d4cef2..8e94109643131 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -24,7 +24,9 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.car.navigationbar.CarNavigationBarController; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -47,11 +49,16 @@ public class OverlayViewGlobalStateController { private static final int UNKNOWN_Z_ORDER = -1; private final SystemUIOverlayWindowController mSystemUIOverlayWindowController; private final CarNavigationBarController mCarNavigationBarController; + + private boolean mIsOccluded; + @VisibleForTesting Map mZOrderMap; @VisibleForTesting SortedMap mZOrderVisibleSortedMap; @VisibleForTesting + Set mViewsHiddenForOcclusion; + @VisibleForTesting OverlayViewController mHighestZOrder; @Inject @@ -63,6 +70,7 @@ public class OverlayViewGlobalStateController { mCarNavigationBarController = carNavigationBarController; mZOrderMap = new HashMap<>(); mZOrderVisibleSortedMap = new TreeMap<>(); + mViewsHiddenForOcclusion = new HashSet<>(); } /** @@ -91,6 +99,10 @@ public class OverlayViewGlobalStateController { */ public void showView(OverlayViewController viewController, @Nullable Runnable show) { debugLog(); + if (mIsOccluded && !viewController.shouldShowWhenOccluded()) { + mViewsHiddenForOcclusion.add(viewController); + return; + } if (mZOrderVisibleSortedMap.isEmpty()) { setWindowVisible(true); } @@ -147,6 +159,10 @@ public class OverlayViewGlobalStateController { */ public void hideView(OverlayViewController viewController, @Nullable Runnable hide) { debugLog(); + if (mIsOccluded && mViewsHiddenForOcclusion.contains(viewController)) { + mViewsHiddenForOcclusion.remove(viewController); + return; + } if (!viewController.isInflated()) { Log.d(TAG, "Content cannot be hidden since it isn't inflated: " + viewController.getClass().getName()); @@ -240,6 +256,43 @@ public class OverlayViewGlobalStateController { return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN(); } + /** + * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is + * occluded, all views mounted to it that are not configured to be shown during occlusion will + * be hidden. + */ + public void setOccluded(boolean occluded) { + if (occluded) { + // Hide views before setting mIsOccluded to true so the regular hideView logic is used, + // not the one used during occlusion. + hideViewsForOcclusion(); + mIsOccluded = true; + } else { + mIsOccluded = false; + // show views after setting mIsOccluded to false so the regular showView logic is used, + // not the one used during occlusion. + showViewsHiddenForOcclusion(); + } + } + + private void hideViewsForOcclusion() { + HashSet viewsCurrentlyShowing = new HashSet<>( + mZOrderVisibleSortedMap.values()); + viewsCurrentlyShowing.forEach(overlayController -> { + if (!overlayController.shouldShowWhenOccluded()) { + hideView(overlayController, overlayController::hideInternal); + mViewsHiddenForOcclusion.add(overlayController); + } + }); + } + + private void showViewsHiddenForOcclusion() { + mViewsHiddenForOcclusion.forEach(overlayViewController -> { + showView(overlayViewController, overlayViewController::showInternal); + }); + mViewsHiddenForOcclusion.clear(); + } + private void debugLog() { if (!DEBUG) { return; @@ -250,5 +303,8 @@ public class OverlayViewGlobalStateController { Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap); Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size()); Log.d(TAG, "mZOrderMap: " + mZOrderMap); + Log.d(TAG, "mIsOccluded: " + mIsOccluded); + Log.d(TAG, "mViewsHiddenForOcclusion: " + mViewsHiddenForOcclusion); + Log.d(TAG, "mViewsHiddenForOcclusion.size(): " + mViewsHiddenForOcclusion.size()); } } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java index 38836d85e8d47..189e240169c3d 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -168,6 +169,18 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase { any()); } + @Test + public void setOccludedFalse_currentlyOccluded_bouncerReset() { + when(mBouncer.isSecure()).thenReturn(true); + mCarKeyguardViewController.show(/* options= */ null); + mCarKeyguardViewController.setOccluded(/* occluded= */ true, /* animate= */ false); + reset(mBouncer); + + mCarKeyguardViewController.setOccluded(/* occluded= */ false, /* animate= */ false); + + verify(mBouncer).show(/* resetSecuritySelection= */ true); + } + @Test public void onCancelClicked_callsCancelClickedListener() { when(mBouncer.isSecure()).thenReturn(true); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index 9e6e616e3ccfc..cba42e5a9be43 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -490,6 +490,81 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { verify(mSystemUIOverlayWindowController).setWindowVisible(false); } + @Test + public void setOccludedTrue_viewToHideWhenOccludedVisible_viewHidden() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + + mOverlayViewGlobalStateController.setOccluded(true); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedTrue_viewToNotHideWhenOccludedVisible_viewShown() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true); + + mOverlayViewGlobalStateController.setOccluded(true); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + + @Test + public void hideViewAndThenSetOccludedTrue_viewHiddenForOcclusion_viewHiddenAfterOcclusion() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + mOverlayViewGlobalStateController.setOccluded(true); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController1, /* runnable= */ null); + mOverlayViewGlobalStateController.setOccluded(false); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedTrueAndThenShowView_viewToNotHideForOcclusion_viewShown() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true); + + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + + @Test + public void setOccludedTrueAndThenShowView_viewToHideForOcclusion_viewHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedFalse_viewShownAfterSetOccludedTrue_viewToHideForOcclusion_viewShown() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + mOverlayViewGlobalStateController.setOccluded(false); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + @Test public void inflateView_notInflated_inflates() { when(mOverlayViewController2.isInflated()).thenReturn(false);