Refine the onTouch logic and log touch speed

Bug: 180668977
Test: atest UdfpsControllerTest
Test: on device
Change-Id: I097eaf6f627adfc4c916672dd034d0a63a57b32b
This commit is contained in:
Ilya Matyukhin
2021-03-04 10:51:06 -08:00
parent 4c38e34d86
commit 5bb0f5a1f6
3 changed files with 100 additions and 26 deletions

View File

@@ -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,46 +148,98 @@ 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) {
mView.onStateChanged(newState);
mView.onStateChanged(newState);
}
};
};
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:
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) {
onFingerUp();
// 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();
}
return true;
// 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 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();
}
}
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

View File

@@ -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;

View File

@@ -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());