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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user