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
This commit is contained in:
kwaky
2020-05-28 11:55:54 -07:00
parent 82352f9db8
commit 5dc67de144
5 changed files with 159 additions and 5 deletions

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap;
@VisibleForTesting
Set<OverlayViewController> 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<OverlayViewController> 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());
}
}

View File

@@ -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);

View File

@@ -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);