Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +40 −12 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROL import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import android.content.BroadcastReceiver; import android.content.Context; Loading @@ -50,6 +51,7 @@ import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; Loading Loading @@ -234,6 +236,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { public static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); public static final int LONG_PRESS = HapticFeedbackConstants.LONG_PRESS; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurnedOn() { Loading Loading @@ -926,6 +930,17 @@ public class UdfpsController implements DozeReceiver, Dumpable { @VisibleForTesting public void playStartHaptic() { if (mAccessibilityManager.isTouchExplorationEnabled()) { if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { if (mOverlay != null && mOverlay.getOverlayView() != null) { mVibrator.performHapticFeedback( mOverlay.getOverlayView(), HapticFeedbackConstants.CONTEXT_CLICK ); } else { Log.e(TAG, "No haptics played. Could not obtain overlay view to perform" + "vibration. Either the controller overlay is null or has no view"); } } else { mVibrator.vibrate( Process.myUid(), mContext.getOpPackageName(), Loading @@ -934,6 +949,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { UDFPS_VIBRATION_ATTRIBUTES); } } } @Override public void dozeTimeTick() { Loading Loading @@ -1024,12 +1040,24 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardViewManager.showPrimaryBouncer(true); // play the same haptic as the LockIconViewController longpress if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { if (mOverlay != null && mOverlay.getOverlayView() != null) { mVibrator.performHapticFeedback( mOverlay.getOverlayView(), UdfpsController.LONG_PRESS ); } else { Log.e(TAG, "No haptics played. Could not obtain overlay view to perform" + "vibration. Either the controller overlay is null or has no view"); } } else { mVibrator.vibrate( Process.myUid(), mContext.getOpPackageName(), UdfpsController.EFFECT_CLICK, "aod-lock-icon-longpress", LOCK_ICON_VIBRATION_ATTRIBUTES); } return; } Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +102 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; Loading Loading @@ -61,6 +62,7 @@ import android.os.RemoteException; import android.os.VibrationAttributes; import android.testing.TestableLooper.RunWithLooper; import android.util.Pair; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; Loading Loading @@ -1127,6 +1129,36 @@ public class UdfpsControllerTest extends SysuiTestCase { UdfpsController.UDFPS_VIBRATION_ATTRIBUTES.getUsage()); } @Test public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing and a11y touch exploration enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_HOVER is received verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); enterEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN context click haptic is played verify(mVibrator).performHapticFeedback( any(), eq(HapticFeedbackConstants.CONTEXT_CLICK) ); } @Test public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { // Configure UdfpsView to accept the ACTION_DOWN event Loading Loading @@ -1159,6 +1191,35 @@ public class UdfpsControllerTest extends SysuiTestCase { any()); } @Test public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mBiometricExecutor.runAllReady(); moveEvent.recycle(); // THEN NO haptic played verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); } @Test public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath() throws RemoteException { Loading Loading @@ -1514,4 +1575,45 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN is fingerDown should be FALSE assertFalse(mUdfpsController.isFingerDown()); } @Test public void playHaptic_onAodInterrupt_oneWayHapticsDisabled_onAcquiredBad_usesVibrate() throws RemoteException { // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); mScreenObserver.onScreenTurnedOn(); mUdfpsController.onAodInterrupt(0, 0, 0, 0); // THEN vibrate is used verify(mVibrator).vibrate( anyInt(), anyString(), eq(UdfpsController.EFFECT_CLICK), eq("aod-lock-icon-longpress"), eq(UdfpsController.LOCK_ICON_VIBRATION_ATTRIBUTES) ); } @Test public void playHaptic_onAodInterrupt_oneWayHapticsEnabled_onAcquiredBad_performHapticFeedback() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); mScreenObserver.onScreenTurnedOn(); mUdfpsController.onAodInterrupt(0, 0, 0, 0); // THEN vibrate is used verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +40 −12 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROL import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import android.content.BroadcastReceiver; import android.content.Context; Loading @@ -50,6 +51,7 @@ import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; Loading Loading @@ -234,6 +236,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { public static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); public static final int LONG_PRESS = HapticFeedbackConstants.LONG_PRESS; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurnedOn() { Loading Loading @@ -926,6 +930,17 @@ public class UdfpsController implements DozeReceiver, Dumpable { @VisibleForTesting public void playStartHaptic() { if (mAccessibilityManager.isTouchExplorationEnabled()) { if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { if (mOverlay != null && mOverlay.getOverlayView() != null) { mVibrator.performHapticFeedback( mOverlay.getOverlayView(), HapticFeedbackConstants.CONTEXT_CLICK ); } else { Log.e(TAG, "No haptics played. Could not obtain overlay view to perform" + "vibration. Either the controller overlay is null or has no view"); } } else { mVibrator.vibrate( Process.myUid(), mContext.getOpPackageName(), Loading @@ -934,6 +949,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { UDFPS_VIBRATION_ATTRIBUTES); } } } @Override public void dozeTimeTick() { Loading Loading @@ -1024,12 +1040,24 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardViewManager.showPrimaryBouncer(true); // play the same haptic as the LockIconViewController longpress if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { if (mOverlay != null && mOverlay.getOverlayView() != null) { mVibrator.performHapticFeedback( mOverlay.getOverlayView(), UdfpsController.LONG_PRESS ); } else { Log.e(TAG, "No haptics played. Could not obtain overlay view to perform" + "vibration. Either the controller overlay is null or has no view"); } } else { mVibrator.vibrate( Process.myUid(), mContext.getOpPackageName(), UdfpsController.EFFECT_CLICK, "aod-lock-icon-longpress", LOCK_ICON_VIBRATION_ATTRIBUTES); } return; } Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +102 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; Loading Loading @@ -61,6 +62,7 @@ import android.os.RemoteException; import android.os.VibrationAttributes; import android.testing.TestableLooper.RunWithLooper; import android.util.Pair; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; Loading Loading @@ -1127,6 +1129,36 @@ public class UdfpsControllerTest extends SysuiTestCase { UdfpsController.UDFPS_VIBRATION_ATTRIBUTES.getUsage()); } @Test public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing and a11y touch exploration enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_HOVER is received verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); enterEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0); mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN context click haptic is played verify(mVibrator).performHapticFeedback( any(), eq(HapticFeedbackConstants.CONTEXT_CLICK) ); } @Test public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { // Configure UdfpsView to accept the ACTION_DOWN event Loading Loading @@ -1159,6 +1191,35 @@ public class UdfpsControllerTest extends SysuiTestCase { any()); } @Test public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // Configure UdfpsView to accept the ACTION_DOWN event when(mUdfpsView.isDisplayConfigured()).thenReturn(false); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); mBiometricExecutor.runAllReady(); downEvent.recycle(); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); mBiometricExecutor.runAllReady(); moveEvent.recycle(); // THEN NO haptic played verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); } @Test public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath() throws RemoteException { Loading Loading @@ -1514,4 +1575,45 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN is fingerDown should be FALSE assertFalse(mUdfpsController.isFingerDown()); } @Test public void playHaptic_onAodInterrupt_oneWayHapticsDisabled_onAcquiredBad_usesVibrate() throws RemoteException { // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); mScreenObserver.onScreenTurnedOn(); mUdfpsController.onAodInterrupt(0, 0, 0, 0); // THEN vibrate is used verify(mVibrator).vibrate( anyInt(), anyString(), eq(UdfpsController.EFFECT_CLICK), eq("aod-lock-icon-longpress"), eq(UdfpsController.LOCK_ICON_VIBRATION_ATTRIBUTES) ); } @Test public void playHaptic_onAodInterrupt_oneWayHapticsEnabled_onAcquiredBad_performHapticFeedback() throws RemoteException { when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true); // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); mScreenObserver.onScreenTurnedOn(); mUdfpsController.onAodInterrupt(0, 0, 0, 0); // THEN vibrate is used verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); } }