Add minimum show time for the udfps bouncer.

So the udfps bouncer will show for a minimum of 200ms before
an outside touch will dismiss it and show the input
(pin/pattern/password) bouncer.

Also, make sure to intercept all touches when the udfps (alt) bouncer is
showing so that touches don't get propagated to the
ExpandableNotificationRow.

Test: atest UdfpsKeyguardViewControllerTest, NotificationShadeWindowViewTest.java
Fixes: 195612268
Change-Id: I737c9086d1debafb8c7024c035012602f4bdbee4
This commit is contained in:
Beverly
2021-08-16 13:37:44 -04:00
committed by Beverly Tai
parent 0d1290e7c3
commit 2bd5c1f169
6 changed files with 161 additions and 9 deletions

View File

@@ -43,7 +43,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -76,6 +75,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.time.SystemClock;
import java.util.Optional;
@@ -125,6 +125,7 @@ public class UdfpsController implements DozeReceiver {
@Nullable private final UdfpsHbmProvider mHbmProvider;
@NonNull private final KeyguardBypassController mKeyguardBypassController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -449,19 +450,19 @@ public class UdfpsController implements DozeReceiver {
final String touchInfo = String.format(
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mGoodCaptureReceived &&
!exceedsVelocityThreshold) {
onFingerDown((int) event.getRawX(), (int) event.getRawY(), minor,
major);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = SystemClock.elapsedRealtime();
mPowerManager.userActivity(SystemClock.uptimeMillis(),
mTouchLogTime = mSystemClock.elapsedRealtime();
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
mTouchLogTime = SystemClock.elapsedRealtime();
mTouchLogTime = mSystemClock.elapsedRealtime();
}
} else {
Log.v(TAG, "onTouch | finger outside");
@@ -525,7 +526,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull KeyguardBypassController keyguardBypassController,
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@NonNull ConfigurationController configurationController) {
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock) {
mContext = context;
mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
@@ -561,6 +563,7 @@ public class UdfpsController implements DozeReceiver {
mainHandler);
mKeyguardBypassController = keyguardBypassController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -781,6 +784,7 @@ public class UdfpsController implements DozeReceiver {
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
this
);
case IUdfpsOverlayController.REASON_AUTH_BP:

View File

@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,6 +51,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final UdfpsController mUdfpsController;
private boolean mShowingUdfpsBouncer;
@@ -59,6 +61,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
private int mStatusBarState;
private float mTransitionToFullShadeProgress;
private float mLastDozeAmount;
private long mLastUdfpsBouncerShowTime = -1;
/**
* hidden amount of pin/pattern/password bouncer
@@ -79,6 +82,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBar, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -87,6 +91,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mKeyguardViewMediator = keyguardViewMediator;
mLockScreenShadeTransitionController = transitionController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
mUdfpsController = udfpsController;
}
@@ -155,6 +160,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
mShowingUdfpsBouncer = show;
if (mShowingUdfpsBouncer) {
mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
}
updatePauseAuth();
if (mShowingUdfpsBouncer) {
if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
@@ -218,15 +226,24 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
* If we were previously showing the udfps bouncer, hide it and instead show the regular
* (pin/pattern/password) bouncer.
*
* Does nothing if we weren't previously showing the udfps bouncer.
* Does nothing if we weren't previously showing the UDFPS bouncer.
*/
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer) {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
mKeyguardViewManager.resetAlternateAuth(false);
}
}
/**
* Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
* of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
* bouncer.
*/
private boolean hasUdfpsBouncerShownWithMinTime() {
return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200;
}
/**
* Set the progress we're currently transitioning to the full shade. 0.0f means we're not
* transitioning yet, while 1.0f means we've fully dragged down.

View File

@@ -311,6 +311,12 @@ public class NotificationShadeWindowViewController {
// Capture all touch events in always-on.
return true;
}
if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
// capture all touches if the alt auth bouncer is showing
return true;
}
boolean intercept = false;
if (mNotificationPanelViewController.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
@@ -338,6 +344,12 @@ public class NotificationShadeWindowViewController {
if (mStatusBarStateController.isDozing()) {
handled = !mService.isPulsing();
}
if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
// eat the touch
handled = true;
}
if ((mDragDownHelper.isDragDownEnabled() && !handled)
|| mDragDownHelper.isDraggingDown()) {
// we still want to finish our drag down gesture when locking the screen

View File

@@ -69,6 +69,7 @@ import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import org.junit.Before;
import org.junit.Rule;
@@ -147,6 +148,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private Handler mHandler;
@Mock
private ConfigurationController mConfigurationController;
@Mock
private SystemClock mSystemClock;
private FakeExecutor mFgExecutor;
@@ -229,7 +232,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mKeyguardBypassController,
mDisplayManager,
mHandler,
mConfigurationController);
mConfigurationController,
mSystemClock);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());

View File

@@ -18,8 +18,11 @@ package com.android.systemui.biometrics;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,6 +44,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -80,6 +84,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
private ConfigurationController mConfigurationController;
@Mock
private UdfpsController mUdfpsController;
private FakeSystemClock mSystemClock = new FakeSystemClock();
private UdfpsKeyguardViewController mController;
@@ -114,6 +119,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
mUdfpsController);
}
@@ -273,6 +279,59 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor);
}
@Test
public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() {
// GIVEN view is attached
mController.onViewAttached();
captureAltAuthInterceptor();
// GIVEN udfps bouncer isn't showing
mAltAuthInterceptor.hideAlternateAuthBouncer();
// WHEN touch is observed outside the view
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
}
@Test
public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() {
// GIVEN view is attached
mController.onViewAttached();
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
mAltAuthInterceptor.showAlternateAuthBouncer();
// WHEN touch is observed outside the view 200ms later (just within threshold)
mSystemClock.advanceTime(200);
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called because not enough time has passed
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
}
@Test
public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() {
// GIVEN view is attached
mController.onViewAttached();
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
mAltAuthInterceptor.showAlternateAuthBouncer();
// WHEN touch is observed outside the view 205ms later
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
// THEN show the bouncer and reset alt auth
verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
}
private void sendStatusBarStateChanged(int statusBarState) {
mStatusBarStateListener.onStateChanged(statusBarState);
}

View File

@@ -16,6 +16,11 @@
package com.android.systemui.statusbar.phone;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -55,6 +60,8 @@ import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -91,6 +98,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -147,4 +158,49 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
verify(mDragDownHelper).onTouchEvent(ev);
ev.recycle();
}
@Test
public void testInterceptTouchWhenShowingAltAuth() {
captureInteractionEventHandler();
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should intercept touch
assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
}
@Test
public void testNoInterceptTouch() {
captureInteractionEventHandler();
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we shouldn't intercept touch
assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
}
@Test
public void testHandleTouchEventWhenShowingAltAuth() {
captureInteractionEventHandler();
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should handle the touch
assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class)));
}
private void captureInteractionEventHandler() {
verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture());
mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue();
}
}