Merge "Using performHapticFeedback on UdfpsController" into udc-qpr-dev
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ebff0b0..39a45f7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -26,6 +26,7 @@
 
 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;
@@ -50,6 +51,7 @@
 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;
@@ -234,6 +236,8 @@
     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() {
@@ -926,12 +930,24 @@
     @VisibleForTesting
     public void playStartHaptic() {
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    EFFECT_CLICK,
-                    "udfps-onStart-click",
-                    UDFPS_VIBRATION_ATTRIBUTES);
+            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(),
+                        EFFECT_CLICK,
+                        "udfps-onStart-click",
+                        UDFPS_VIBRATION_ATTRIBUTES);
+            }
         }
     }
 
@@ -1024,12 +1040,24 @@
             mKeyguardViewManager.showPrimaryBouncer(true);
 
             // play the same haptic as the LockIconViewController longpress
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    UdfpsController.EFFECT_CLICK,
-                    "aod-lock-icon-longpress",
-                    LOCK_ICON_VIBRATION_ATTRIBUTES);
+            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;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 58982d1..7ab8e8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -23,6 +23,7 @@
 
 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;
@@ -61,6 +62,7 @@
 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;
@@ -1128,6 +1130,36 @@
     }
 
     @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
         when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
@@ -1160,6 +1192,35 @@
     }
 
     @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 {
         // Disable new touch detection.
@@ -1514,4 +1575,45 @@
         // 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));
+    }
 }