Merge "Ignore the closing MotionEvent of swipe gestures for falsing" into tm-qpr-dev

This commit is contained in:
András Kurucz
2023-03-03 19:08:13 +00:00
committed by Android (Google) Code Review
5 changed files with 80 additions and 10 deletions

View File

@@ -38,6 +38,7 @@ import javax.inject.Inject;
public class FalsingDataProvider {
private static final long MOTION_EVENT_AGE_MS = 1000;
private static final long DROP_EVENT_THRESHOLD_MS = 50;
private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
private final int mWidthPixels;
@@ -60,6 +61,7 @@ public class FalsingDataProvider {
private float mAngle = 0;
private MotionEvent mFirstRecentMotionEvent;
private MotionEvent mLastMotionEvent;
private boolean mDropLastEvent;
private boolean mJustUnlockedWithFace;
private boolean mA11YAction;
@@ -95,6 +97,12 @@ public class FalsingDataProvider {
// Ensure prior gesture was completed. May be a no-op.
completePriorGesture();
}
// Drop the gesture closing event if it is close in time to a previous ACTION_MOVE event.
// The reason is that the closing ACTION_UP event of a swipe can be a bit offseted from the
// previous ACTION_MOVE event and when it happens, it makes some classifiers fail.
mDropLastEvent = shouldDropEvent(motionEvent);
mRecentMotionEvents.addAll(motionEvents);
FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size());
@@ -129,6 +137,7 @@ public class FalsingDataProvider {
mPriorMotionEvents = mRecentMotionEvents;
mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
}
mDropLastEvent = false;
mA11YAction = false;
}
@@ -150,8 +159,18 @@ public class FalsingDataProvider {
return mYdpi;
}
/**
* Get the {@link MotionEvent}s of the most recent gesture.
*
* Note that this list may not include the last recorded event.
* @see #mDropLastEvent
*/
public List<MotionEvent> getRecentMotionEvents() {
return mRecentMotionEvents;
if (!mDropLastEvent || mRecentMotionEvents.isEmpty()) {
return mRecentMotionEvents;
} else {
return mRecentMotionEvents.subList(0, mRecentMotionEvents.size() - 1);
}
}
public List<MotionEvent> getPriorMotionEvents() {
@@ -169,7 +188,12 @@ public class FalsingDataProvider {
return mFirstRecentMotionEvent;
}
/** Get the last recorded {@link MotionEvent}. */
/**
* Get the last {@link MotionEvent} of the most recent gesture.
*
* Note that this may be the event prior to the last recorded event.
* @see #mDropLastEvent
*/
public MotionEvent getLastMotionEvent() {
recalculateData();
return mLastMotionEvent;
@@ -236,12 +260,13 @@ public class FalsingDataProvider {
return;
}
if (mRecentMotionEvents.isEmpty()) {
List<MotionEvent> recentMotionEvents = getRecentMotionEvents();
if (recentMotionEvents.isEmpty()) {
mFirstRecentMotionEvent = null;
mLastMotionEvent = null;
} else {
mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
mFirstRecentMotionEvent = recentMotionEvents.get(0);
mLastMotionEvent = recentMotionEvents.get(recentMotionEvents.size() - 1);
}
calculateAngleInternal();
@@ -249,6 +274,17 @@ public class FalsingDataProvider {
mDirty = false;
}
private boolean shouldDropEvent(MotionEvent event) {
if (mRecentMotionEvents.size() < 3) return false;
MotionEvent lastEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
boolean isCompletingGesture = event.getActionMasked() == MotionEvent.ACTION_UP
&& lastEvent.getActionMasked() == MotionEvent.ACTION_MOVE;
boolean isRecentEvent =
event.getEventTime() - lastEvent.getEventTime() < DROP_EVENT_THRESHOLD_MS;
return isCompletingGesture && isRecentEvent;
}
private void calculateAngleInternal() {
if (mRecentMotionEvents.size() < 2) {
mAngle = Float.MAX_VALUE;

View File

@@ -183,7 +183,7 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
@Override
public List<MotionEvent> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
return mMotionEvents.subList(fromIndex, toIndex);
}
class Iter implements ListIterator<MotionEvent> {

View File

@@ -94,7 +94,9 @@ public class DistanceClassifierTest extends ClassifierTest {
mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3));
mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300));
mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301));
mClassifier.onTouchEvent(appendUpEvent(1, 19, 501));
mClassifier.onTouchEvent(appendMoveEvent(1, 19, 501));
mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); //event will be dropped
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
}

View File

@@ -75,16 +75,17 @@ public class FalsingDataProviderTest extends ClassifierTest {
}
@Test
public void test_trackMotionEvents() {
public void test_trackMotionEvents_dropUpEvent() {
mDataProvider.onMotionEvent(appendDownEvent(2, 9));
mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
mDataProvider.onMotionEvent(appendUpEvent(6, 5));
mDataProvider.onMotionEvent(appendMoveEvent(6, 5));
mDataProvider.onMotionEvent(appendUpEvent(0, 0)); // event will be dropped
List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
assertThat(motionEventList.size()).isEqualTo(3);
assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN);
assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP);
assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L);
assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L);
assertThat(motionEventList.get(2).getEventTime()).isEqualTo(3L);
@@ -96,6 +97,28 @@ public class FalsingDataProviderTest extends ClassifierTest {
assertThat(motionEventList.get(2).getY()).isEqualTo(5f);
}
@Test
public void test_trackMotionEvents_keepUpEvent() {
mDataProvider.onMotionEvent(appendDownEvent(2, 9));
mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
mDataProvider.onMotionEvent(appendUpEvent(0, 0, 100));
List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
assertThat(motionEventList.size()).isEqualTo(3);
assertThat(motionEventList.get(0).getActionMasked()).isEqualTo(MotionEvent.ACTION_DOWN);
assertThat(motionEventList.get(1).getActionMasked()).isEqualTo(MotionEvent.ACTION_MOVE);
assertThat(motionEventList.get(2).getActionMasked()).isEqualTo(MotionEvent.ACTION_UP);
assertThat(motionEventList.get(0).getEventTime()).isEqualTo(1L);
assertThat(motionEventList.get(1).getEventTime()).isEqualTo(2L);
assertThat(motionEventList.get(2).getEventTime()).isEqualTo(100);
assertThat(motionEventList.get(0).getX()).isEqualTo(2f);
assertThat(motionEventList.get(1).getX()).isEqualTo(4f);
assertThat(motionEventList.get(2).getX()).isEqualTo(0f);
assertThat(motionEventList.get(0).getY()).isEqualTo(9f);
assertThat(motionEventList.get(1).getY()).isEqualTo(7f);
assertThat(motionEventList.get(2).getY()).isEqualTo(0f);
}
@Test
public void test_trackRecentMotionEvents() {
mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1));

View File

@@ -67,6 +67,15 @@ public class ZigZagClassifierTest extends ClassifierTest {
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
}
@Test
public void testPass_dropClosingUpEvent() {
appendMoveEvent(0, 0);
appendMoveEvent(0, 100);
appendMoveEvent(0, 200);
appendUpEvent(0, 180); // this event would push us over the maxDevianceY
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
}
@Test
public void testPass_fewTouchesHorizontal() {
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();