Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bb0f5a1 authored by Ilya Matyukhin's avatar Ilya Matyukhin
Browse files

Refine the onTouch logic and log touch speed

Bug: 180668977
Test: atest UdfpsControllerTest
Test: on device
Change-Id: I097eaf6f627adfc4c916672dd034d0a63a57b32b
parent 4c38e34d
Loading
Loading
Loading
Loading
+84 −20
Original line number Diff line number Diff line
@@ -28,11 +28,13 @@ import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.WindowManager;

import androidx.annotation.NonNull;
@@ -66,6 +68,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
    private static final String TAG = "UdfpsController";
    private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;

    // Minimum required delay between consecutive touch logs in milliseconds.
    private static final long MIN_TOUCH_LOG_INTERVAL = 50;

    private final Context mContext;
    private final FingerprintManager mFingerprintManager;
    @NonNull private final LayoutInflater mInflater;
@@ -78,6 +83,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
    @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
    private final WindowManager.LayoutParams mCoreLayoutParams;

    // Tracks the velocity of a touch to help filter out the touches that move too fast.
    @Nullable private VelocityTracker mVelocityTracker;
    // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
    private int mActivePointerId;
    // The timestamp of the most recent touch log.
    private long mTouchLogTime;

    @Nullable private UdfpsView mView;
    // Indicates whether the overlay has been requested.
    private boolean mIsOverlayRequested;
@@ -136,12 +148,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
        }
    }

    @VisibleForTesting
    final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
    @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
            (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);

    @VisibleForTesting
    final StatusBarStateController.StateListener mStatusBarStateListener =
    @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener =
            new StatusBarStateController.StateListener() {
                @Override
                public void onStateChanged(int newState) {
@@ -149,33 +159,87 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                }
            };

    private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
        final float vx = tracker.getXVelocity(pointerId);
        final float vy = tracker.getYVelocity(pointerId);
        return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
    }

    @SuppressLint("ClickableViewAccessibility")
    private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
        UdfpsView view = (UdfpsView) v;
        final boolean isFingerDown = view.isIlluminationRequested();
        switch (event.getAction()) {
    private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) -> {
        UdfpsView udfpsView = (UdfpsView) view;
        final boolean isFingerDown = udfpsView.isIlluminationRequested();
        boolean handled = false;
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                // To simplify the lifecycle of the velocity tracker, make sure it's never null
                // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                } else {
                    // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
                    // ACTION_DOWN, in that case we should just reuse the old instance.
                    mVelocityTracker.clear();
                }
                // TODO: move isWithinSensorArea to UdfpsController.
                if (udfpsView.isWithinSensorArea(event.getX(), event.getY())) {
                    // The pointer that causes ACTION_DOWN is always at index 0.
                    // We need to persist its ID to track it during ACTION_MOVE that could include
                    // data for many other pointers because of multi-touch support.
                    mActivePointerId = event.getPointerId(0);
                    mVelocityTracker.addMovement(event);
                    handled = true;
                }
                break;

            case MotionEvent.ACTION_MOVE:
                final boolean isValidTouch = view.isValidTouch(event.getX(), event.getY(),
                        event.getPressure());
                if (!isFingerDown && isValidTouch) {
                    onFingerDown((int) event.getX(), (int) event.getY(), event.getTouchMinor(),
                            event.getTouchMajor());
                } else if (isFingerDown && !isValidTouch) {
                final int idx = event.findPointerIndex(mActivePointerId);
                if (idx == event.getActionIndex()) {
                    final float x = event.getX(idx);
                    final float y = event.getY(idx);
                    if (udfpsView.isWithinSensorArea(x, y)) {
                        mVelocityTracker.addMovement(event);
                        // Compute pointer velocity in pixels per second.
                        mVelocityTracker.computeCurrentVelocity(1000);
                        // Compute pointer speed from X and Y velocities.
                        final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
                        final float minor = event.getTouchMinor(idx);
                        final float major = event.getTouchMajor(idx);
                        final String touchInfo = String.format("minor: %.1f, major: %.1f, v: %.1f",
                                minor, major, v);
                        final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
                        if (!isFingerDown) {
                            onFingerDown((int) x, (int) y, minor, major);
                            Log.v(TAG, "onTouch | finger down: " + touchInfo);
                            mTouchLogTime = SystemClock.elapsedRealtime();
                            handled = true;
                        } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
                            Log.v(TAG, "onTouch | finger move: " + touchInfo);
                            mTouchLogTime = SystemClock.elapsedRealtime();
                        }
                    } else if (isFingerDown) {
                        Log.v(TAG, "onTouch | finger outside");
                        onFingerUp();
                    }
                return true;
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                if (isFingerDown) {
                    Log.v(TAG, "onTouch | finger up");
                    onFingerUp();
                }
                return true;
                break;

            default:
                return false;
                // Do nothing.
        }
        return handled;
    };

    @Inject
+8 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

@@ -92,6 +93,12 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
        mIlluminationRequested = false;
    }

    // Don't propagate any touch events to the child views.
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    protected void onFinishInflate() {
        mHbmSurfaceView = findViewById(R.id.hbm_view);
@@ -179,7 +186,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
        postInvalidate();
    }

    boolean isValidTouch(float x, float y, float pressure) {
    boolean isWithinSensorArea(float x, float y) {
        // The X and Y coordinates of the sensor's center.
        final PointF translation = mAnimationView.getTouchTranslation();
        final float cx = mSensorRect.centerX() + translation.x;
+7 −4
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
    public void fingerDown() throws RemoteException {
        // Configure UdfpsView to accept the ACTION_DOWN event
        when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
        when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);

        // GIVEN that the overlay is showing
        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
@@ -187,9 +187,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
        mFgExecutor.runAllReady();
        // WHEN ACTION_DOWN is received
        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
        event.recycle();
        MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
        downEvent.recycle();
        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
        moveEvent.recycle();
        // THEN illumination begins
        // AND onIlluminatedRunnable that notifies FingerprintManager is set
        verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());