Fix AlternateBouncer touch handling

- Only handle altBouncer touches if up & down received
- If UDFPS is enrolled and new touch handling is enabled,
the UdfpsController will forward touches to the
StatusBarKeyguardViewManager to handle alternateBouncer
touches.

Test: atest StatusBarKeyguardViewManagerTest
 NotificationShadeWindowViewTest UdfpsOverlayInteractorTest
 NotificationShadeWindowViewControllerTest
Test: Use the alternateBouncer from an occluding app
like the camera (double press power button from LS)
Fixes: 273532029
Bug: 270285915
Bug: 272744555

Change-Id: I57bab58a47b9227fc13876edd3f6af22a840c147
This commit is contained in:
Beverly
2023-03-10 16:28:51 +00:00
committed by Beverly Tai
parent a31862ea01
commit a439c28821
10 changed files with 245 additions and 76 deletions

View File

@@ -823,7 +823,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
final Rect overlayBounds = new Rect(
0, /* left */
mCachedDisplayInfo.getNaturalHeight() / 2, /* top */
0, /* top */
mCachedDisplayInfo.getNaturalWidth(), /* right */
mCachedDisplayInfo.getNaturalHeight() /* botom */);

View File

@@ -607,7 +607,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
case UNCHANGED:
if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(),
true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID
&& event.getActionMasked() == MotionEvent.ACTION_DOWN
&& mAlternateBouncerInteractor.isVisibleState()) {
// No pointer on sensor, forward to keyguard if alternateBouncer is visible
mKeyguardViewManager.onTouch(event);

View File

@@ -37,12 +37,12 @@ class UdfpsOverlayInteractor
@Inject
constructor(private val authController: AuthController, @Application scope: CoroutineScope) {
/** Whether a touch should be intercepted or allowed to pass to the UdfpsOverlay */
fun canInterceptTouchInUdfpsBounds(ev: MotionEvent): Boolean {
/** Whether a touch is within the under-display fingerprint sensor area */
fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean {
val isUdfpsEnrolled = authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
val isWithinUdfpsOverlayBounds =
val isWithinOverlayBounds =
udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
return !isUdfpsEnrolled || !isWithinUdfpsOverlayBounds
return isUdfpsEnrolled && isWithinOverlayBounds
}
/** Returns the current udfpsOverlayParams */

View File

@@ -46,6 +46,7 @@ constructor(
var legacyAlternateBouncer: LegacyAlternateBouncer? = null
var legacyAlternateBouncerVisibleTime: Long = NOT_VISIBLE
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
/**
@@ -79,6 +80,7 @@ constructor(
* @return true if the alternate bouncer was newly hidden, else false.
*/
fun hide(): Boolean {
receivedDownTouch = false
return if (isModernAlternateBouncerEnabled) {
val wasAlternateBouncerVisible = isVisibleState()
bouncerRepository.setAlternateVisible(false)

View File

@@ -37,14 +37,12 @@ import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -94,8 +92,6 @@ public class NotificationShadeWindowViewController {
private final AmbientState mAmbientState;
private final PulsingGestureListener mPulsingGestureListener;
private final NotificationInsetsController mNotificationInsetsController;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
private final boolean mIsTrackpadCommonEnabled;
private GestureDetector mPulsingWakeupGestureHandler;
private View mBrightnessMirror;
@@ -145,8 +141,6 @@ public class NotificationShadeWindowViewController {
PulsingGestureListener pulsingGestureListener,
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
UdfpsOverlayInteractor udfpsOverlayInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
FeatureFlags featureFlags,
@@ -170,8 +164,6 @@ public class NotificationShadeWindowViewController {
mAmbientState = ambientState;
mPulsingGestureListener = pulsingGestureListener;
mNotificationInsetsController = notificationInsetsController;
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
// This view is not part of the newly inflated expanded status bar.
@@ -269,6 +261,9 @@ public class NotificationShadeWindowViewController {
mFalsingCollector.onTouchEvent(ev);
mPulsingWakeupGestureHandler.onTouchEvent(ev);
if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) {
return true;
}
if (mBrightnessMirror != null
&& mBrightnessMirror.getVisibility() == View.VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that
@@ -343,9 +338,10 @@ public class NotificationShadeWindowViewController {
return true;
}
if (mAlternateBouncerInteractor.isVisibleState()) {
// If using UDFPS, don't intercept touches that are within its overlay bounds
return mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(ev);
if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) {
// Don't allow touches to proceed to underlying views if alternate
// bouncer is showing
return true;
}
if (mLockIconViewController.onInterceptTouchEvent(ev)) {
@@ -381,10 +377,8 @@ public class NotificationShadeWindowViewController {
handled = !mService.isPulsing();
}
if (mAlternateBouncerInteractor.isVisibleState()) {
// eat the touch
mStatusBarKeyguardViewManager.onTouch(ev);
handled = true;
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
if ((mDragDownHelper.isDragDownEnabled() && !handled)

View File

@@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
@@ -282,6 +283,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
private boolean mIsModernAlternateBouncerEnabled;
private boolean mIsBackAnimationEnabled;
private final boolean mUdfpsNewTouchDetectionEnabled;
private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -336,7 +339,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
PrimaryBouncerInteractor primaryBouncerInteractor,
BouncerView primaryBouncerView,
AlternateBouncerInteractor alternateBouncerInteractor) {
AlternateBouncerInteractor alternateBouncerInteractor,
UdfpsOverlayInteractor udfpsOverlayInteractor
) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -362,6 +367,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAlternateBouncerInteractor = alternateBouncerInteractor;
mIsBackAnimationEnabled =
featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
mUdfpsNewTouchDetectionEnabled = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION);
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
}
@Override
@@ -1442,17 +1449,49 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
/**
* An opportunity for the AlternateBouncer to handle the touch instead of sending
* the touch to NPVC child views.
* @return true if the alternate bouncer should consime the touch and prevent it from
* going to its child views
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if (shouldInterceptTouchEvent(event)
&& !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) {
onTouch(event);
}
return shouldInterceptTouchEvent(event);
}
/**
* Whether the touch should be intercepted by the AlternateBouncer before going to the
* notification shade's child views.
*/
public boolean shouldInterceptTouchEvent(MotionEvent event) {
return mAlternateBouncerInteractor.isVisibleState();
}
/**
* For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
* showing.
*/
public boolean onTouch(MotionEvent event) {
boolean handledTouch = false;
if (event.getAction() == MotionEvent.ACTION_UP
&& mAlternateBouncerInteractor.isVisibleState()
&& mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) {
showPrimaryBouncer(true);
handledTouch = true;
boolean handleTouch = shouldInterceptTouchEvent(event);
if (handleTouch) {
final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
&& event.getActionMasked() == MotionEvent.ACTION_UP;
final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
mUdfpsNewTouchDetectionEnabled && mKeyguardUpdateManager.isUdfpsEnrolled();
final boolean actionOutsideShouldDismissAlternateBouncer =
event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
&& !udfpsOverlayWillForwardEventsOutsideNotificationShade;
if (actionDown) {
mAlternateBouncerInteractor.setReceivedDownTouch(true);
} else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer)
&& mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) {
showPrimaryBouncer(true);
}
}
// Forward NPVC touches to callbacks in case they want to respond to touches
@@ -1460,7 +1499,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
callback.onTouch(event);
}
return handledTouch;
return handleTouch;
}
/** Update keyguard position based on a tapped X coordinate. */

View File

@@ -77,16 +77,14 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
runCurrent()
// Then touch should not be intercepted
val canInterceptTrue = underTest.canInterceptTouchInUdfpsBounds(downEv)
assertThat(canInterceptTrue).isFalse()
// Then touch is within udfps area
assertThat(underTest.isTouchWithinUdfpsArea(downEv)).isTrue()
// When touch is outside of bounds
whenever(overlayBounds.contains(downEv.x.toInt(), downEv.y.toInt())).thenReturn(false)
// Then touch should be intercepted
val canInterceptFalse = underTest.canInterceptTouchInUdfpsBounds(downEv)
assertThat(canInterceptFalse).isTrue()
// Then touch is not within udfps area
assertThat(underTest.isTouchWithinUdfpsArea(downEv)).isFalse()
}
@Test

View File

@@ -26,13 +26,11 @@ import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
@@ -69,8 +67,8 @@ import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -97,8 +95,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -155,8 +151,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
pulsingGestureListener,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
alternateBouncerInteractor,
udfpsOverlayInteractor,
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
@@ -311,17 +305,15 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
}
@Test
fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() =
testScope.runTest {
// Down event within udfpsOverlay bounds while alternateBouncer is showing
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(DOWN_EVENT))
.thenReturn(false)
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
// down event should be intercepted by keyguardViewManager
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
.thenReturn(true)
// Then touch should not be intercepted
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
assertThat(shouldIntercept).isFalse()
}
// Then touch should not be intercepted
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
assertThat(shouldIntercept).isTrue()
}
@Test
fun testGetBouncerContainer() =

View File

@@ -26,13 +26,11 @@ import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -103,14 +101,12 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock
private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
private lateinit var primaryBouncerToGoneTransitionViewModel:
PrimaryBouncerToGoneTransitionViewModel
@Captor
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
private lateinit var underTest: NotificationShadeWindowView
private lateinit var controller: NotificationShadeWindowViewController
@@ -166,8 +162,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
pulsingGestureListener,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
alternateBouncerInteractor,
udfpsOverlayInteractor,
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
@@ -207,8 +201,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
whenever(statusBarStateController.isDozing).thenReturn(false)
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true)
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true)
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
// THEN we should intercept touch
@@ -222,7 +215,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
whenever(statusBarStateController.isDozing).thenReturn(false)
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(false)
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any()))
.thenReturn(false)
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
// THEN we shouldn't intercept touch
@@ -236,7 +230,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
whenever(statusBarStateController.isDozing).thenReturn(false)
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
// THEN we should handle the touch

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,6 +59,7 @@ import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
@@ -126,12 +128,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
@Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@Mock private BouncerView mBouncerView;
@Mock private BouncerViewDelegate mBouncerViewDelegate;
@Mock private OnBackAnimationCallback mBouncerViewDelegateBackCallback;
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private WindowInsetsController mWindowInsetsController;
@Mock private TaskbarDelegate mTaskbarDelegate;
@Mock private StatusBarKeyguardViewManager.KeyguardViewManagerCallback mCallback;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -188,7 +192,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mPrimaryBouncerCallbackInteractor,
mPrimaryBouncerInteractor,
mBouncerView,
mAlternateBouncerInteractor) {
mAlternateBouncerInteractor,
mUdfpsOverlayInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -675,7 +680,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mPrimaryBouncerCallbackInteractor,
mPrimaryBouncerInteractor,
mBouncerView,
mAlternateBouncerInteractor) {
mAlternateBouncerInteractor,
mUdfpsOverlayInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -713,7 +719,115 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
public void testAlternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() {
public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
mStatusBarKeyguardViewManager.addCallback(mCallback);
// GIVEN the alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
// THEN handleDispatchTouchEvent doesn't use the touches
assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
));
assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
));
assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
));
// THEN the touch is not acted upon
verify(mCallback, never()).onTouch(any());
}
@Test
public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() {
mStatusBarKeyguardViewManager.addCallback(mCallback);
// GIVEN the alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// GIVEN all touches are NOT the udfps overlay
when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
// THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
// to its child views (handleDispatchTouchEvent returns true)
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
));
// THEN the touch is acted upon once for each dispatchTOuchEvent call
verify(mCallback, times(3)).onTouch(any());
}
@Test
public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() {
mStatusBarKeyguardViewManager.addCallback(mCallback);
// GIVEN the alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// GIVEN all touches are within the udfps overlay
when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true);
// THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
// to its child views (handleDispatchTouchEvent returns true)
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
));
// THEN the touch is NOT acted upon at the moment
verify(mCallback, never()).onTouch(any());
}
@Test
public void shouldInterceptTouch_alternateBouncerNotVisible() {
// GIVEN the alternate bouncer is not visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
// THEN no motion events are intercepted
assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
));
assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
));
assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
));
}
@Test
public void shouldInterceptTouch_alternateBouncerVisible() {
// GIVEN the alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// THEN all motion events are intercepted
assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
));
assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
));
}
@Test
public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() {
// GIVEN the alternate bouncer has shown and calls to hide() will result in successfully
// hiding it
when(mAlternateBouncerInteractor.hide()).thenReturn(true);
@@ -729,30 +843,67 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
public void testAlternateBouncerOnTouch_actionDown_doesNotHandleTouch() {
public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() {
reset(mAlternateBouncerInteractor);
// GIVEN the alternate bouncer has shown for a minimum amount of time
when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
// WHEN ACTION_DOWN touch event comes
boolean touchHandled = mStatusBarKeyguardViewManager.onTouch(
// WHEN ACTION_DOWN and ACTION_UP touch event comes
boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
// THEN the touch is not handled
assertFalse(touchHandled);
// THEN the touches are handled (doesn't let touches through to underlying views)
assertTrue(touchHandledDown);
assertTrue(touchHandledUp);
// THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met
verify(mAlternateBouncerInteractor, never()).hide();
}
@Test
public void testAlternateBouncerOnTouch_actionUp_handlesTouch() {
public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() {
reset(mAlternateBouncerInteractor);
// GIVEN the alternate bouncer has shown for a minimum amount of time
when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
// WHEN ACTION_UP touch event comes
boolean touchHandled = mStatusBarKeyguardViewManager.onTouch(
// WHEN ACTION_DOWN and ACTION_UP touch event comes
boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
// THEN the touch is handled
assertTrue(touchHandled);
// THEN the touches are handled
assertTrue(touchHandledDown);
assertTrue(touchHandledUp);
// THEN alternate bouncer attempts to hide
verify(mAlternateBouncerInteractor).hide();
}
@Test
public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() {
reset(mAlternateBouncerInteractor);
// GIVEN the alternate bouncer has shown for a minimum amount of time
when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
// WHEN only ACTION_UP touch event comes
mStatusBarKeyguardViewManager.onTouch(
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
// THEN the alternateBouncer doesn't hide
verify(mAlternateBouncerInteractor, never()).hide();
}
}