Make FalsingClassifiers less stateful.

Clasifiers now return a Result object on all their classification
methods. The Result object now contains the "reason" for falsing
instead of asking the Classifier for the reason after the fact.

Bug: 172655679
Test: atest SystemUITests
Change-Id: I0865f18cbae9367c203936fbbc3923de55c81007
This commit is contained in:
Dave Mankoff
2020-11-30 11:19:32 -05:00
parent 3ae5052e7b
commit 1179f84f54
15 changed files with 156 additions and 110 deletions

View File

@@ -137,22 +137,22 @@ public class BrightLineFalsingManager implements FalsingManager {
mPreviousResult = !mTestHarness
&& !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()
&& mClassifiers.stream().anyMatch(falsingClassifier -> {
boolean result = falsingClassifier.classifyGesture(
FalsingClassifier.Result result = falsingClassifier.classifyGesture(
mHistoryTracker.falsePenalty(), mHistoryTracker.falseConfidence());
if (result) {
if (result.isFalse()) {
logInfo(String.format(
(Locale) null,
"{classifier=%s, interactionType=%d}",
falsingClassifier.getClass().getName(),
mDataProvider.getInteractionType()));
String reason = falsingClassifier.getReason();
String reason = result.getReason();
if (reason != null) {
logInfo(reason);
}
} else {
logDebug(falsingClassifier.getClass().getName() + ": false");
}
return result;
return result.isFalse();
});
logDebug("Is false touch? " + mPreviousResult);
@@ -178,10 +178,12 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public boolean isFalseTap(boolean robustCheck) {
if (!mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents())) {
FalsingClassifier.Result singleTapResult =
mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
if (singleTapResult.isFalse()) {
logInfo(String.format(
(Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName()));
String reason = mSingleTapClassifier.getReason();
String reason = singleTapResult.getReason();
if (reason != null) {
logInfo(reason);
}
@@ -198,16 +200,16 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public boolean isFalseDoubleTap() {
boolean result = mDoubleTapClassifier.classifyGesture().isFalse();
if (result) {
FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
if (result.isFalse()) {
logInfo(String.format(
(Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName()));
String reason = mDoubleTapClassifier.getReason();
String reason = result.getReason();
if (reason != null) {
logInfo(reason);
}
}
return result;
return result.isFalse();
}
@Override

View File

@@ -66,12 +66,12 @@ class DiagonalClassifier extends FalsingClassifier {
float angle = getAngle();
if (angle == Float.MAX_VALUE) { // Unknown angle
return new Result(false, 0);
return Result.passed(0);
}
if (getInteractionType() == LEFT_AFFORDANCE
|| getInteractionType() == RIGHT_AFFORDANCE) {
return new Result(false, 0);
return Result.passed(0);
}
float minAngle = DIAGONAL - mHorizontalAngleRange;
@@ -81,15 +81,15 @@ class DiagonalClassifier extends FalsingClassifier {
maxAngle = DIAGONAL + mVerticalAngleRange;
}
return new Result(angleBetween(angle, minAngle, maxAngle)
boolean falsed = angleBetween(angle, minAngle, maxAngle)
|| angleBetween(angle, minAngle + NINETY_DEG, maxAngle + NINETY_DEG)
|| angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
|| angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
maxAngle + ONE_HUNDRED_EIGHTY_DEG), 0.5f);
maxAngle + ONE_HUNDRED_EIGHTY_DEG);
return falsed ? Result.falsed(0.5f, getReason()) : Result.passed(0.5);
}
@Override
String getReason() {
private String getReason() {
return String.format(
(Locale) null,
"{angle=%f, vertical=%s}",

View File

@@ -148,10 +148,10 @@ class DistanceClassifier extends FalsingClassifier {
@Override
Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
return new Result(!getPassedFlingThreshold(), 0.5);
return !getPassedFlingThreshold()
? Result.falsed(0.5, getReason()) : Result.passed(0.5);
}
@Override
String getReason() {
DistanceVectors distanceVectors = getDistances();
@@ -169,10 +169,10 @@ class DistanceClassifier extends FalsingClassifier {
mVerticalSwipeThresholdPx);
}
boolean isLongSwipe() {
Result isLongSwipe() {
boolean longSwipe = getPassedDistanceThreshold();
logDebug("Is longSwipe? " + longSwipe);
return longSwipe;
return longSwipe ? Result.passed(0.5) : Result.falsed(0.5, getReason());
}
private boolean getPassedDistanceThreshold() {

View File

@@ -36,8 +36,6 @@ public class DoubleTapClassifier extends FalsingClassifier {
private final float mDoubleTapSlop;
private final long mDoubleTapTimeMs;
private StringBuilder mReason = new StringBuilder();
@Inject
DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier,
@Named(DOUBLE_TAP_TOUCH_SLOP) float doubleTapSlop,
@@ -54,27 +52,29 @@ public class DoubleTapClassifier extends FalsingClassifier {
Queue<? extends List<MotionEvent>> historicalEvents = getHistoricalEvents();
List<MotionEvent> firstTapEvents = historicalEvents.peek();
mReason = new StringBuilder();
StringBuilder reason = new StringBuilder();
if (firstTapEvents == null) {
mReason.append("Only one gesture recorded");
return new Result(true, 1);
return Result.falsed(1, "Only one gesture recorded");
}
return new Result(!isDoubleTap(firstTapEvents, secondTapEvents, mReason), 0.5);
return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
? Result.falsed(0.5, reason.toString()) : Result.passed(0.5);
}
/** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */
public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents,
StringBuilder reason) {
if (!mSingleTapClassifier.isTap(firstEvents)) {
reason.append("First gesture is not a tap. ").append(mSingleTapClassifier.getReason());
Result firstTap = mSingleTapClassifier.isTap(firstEvents);
if (firstTap.isFalse()) {
reason.append("First gesture is not a tap. ").append(firstTap.getReason());
return false;
}
if (!mSingleTapClassifier.isTap(secondEvents)) {
reason.append("Second gesture is not a tap. ").append(mSingleTapClassifier.getReason());
Result secondTap = mSingleTapClassifier.isTap(secondEvents);
if (secondTap.isFalse()) {
reason.append("Second gesture is not a tap. ").append(secondTap.getReason());
return false;
}
@@ -106,9 +106,4 @@ public class DoubleTapClassifier extends FalsingClassifier {
return true;
}
@Override
String getReason() {
return mReason.toString();
}
}

View File

@@ -120,25 +120,32 @@ public abstract class FalsingClassifier {
void onSessionEnded() {};
/**
* Returns true if the data captured so far looks like a false touch.
* Returns whether a gesture looks like a false touch.
*
* See also {@link #classifyGesture(double, double)}.
*/
Result classifyGesture() {
return calculateFalsingResult(0, 0);
}
boolean classifyGesture(double historyPenalty, double historyConfidence) {
return calculateFalsingResult(historyPenalty, historyConfidence).isFalse();
/**
* Returns whether a gesture looks like a false touch, with the option to consider history.
*
* Unlike the parameter-less version of this method, this method allows the classifier to take
* history into account, penalizing or boosting confidence in a gesture based on recent results.
*
* See also {@link #classifyGesture()}.
*/
Result classifyGesture(double historyPenalty, double historyConfidence) {
return calculateFalsingResult(historyPenalty, historyConfidence);
}
abstract Result calculateFalsingResult(double historyPenalty, double historyConfidence);
/**
* Give the classifier a chance to log more details about why it triggered.
* Calculate a result based on available data.
*
* This should only be called after a call to {@link #classifyGesture()}, and only if
* {@link #classifyGesture()} returns true;
* When passed a historyConfidence of 0, the history penalty should be wholly ignored.
*/
abstract String getReason();
abstract Result calculateFalsingResult(double historyPenalty, double historyConfidence);
/** */
public static void logDebug(String msg) {
@@ -155,13 +162,21 @@ public abstract class FalsingClassifier {
BrightLineFalsingManager.logError(msg);
}
/**
* A Falsing result that encapsulates the boolean result along with confidence and a reason.
*/
static class Result {
private final boolean mFalsed;
private final double mConfidence;
private final String mReason;
Result(boolean falsed, double confidence) {
/**
* See {@link #falsed(double, String)} abd {@link #passed(double)}.
*/
private Result(boolean falsed, double confidence, String reason) {
mFalsed = falsed;
mConfidence = confidence;
mReason = reason;
}
public boolean isFalse() {
@@ -171,5 +186,23 @@ public abstract class FalsingClassifier {
public double getConfidence() {
return mConfidence;
}
public String getReason() {
return mReason;
}
/**
* Construct a "falsed" result indicating that a gesture should be treated as accidental.
*/
static Result falsed(double confidence, String reason) {
return new Result(true, confidence, reason);
}
/**
* Construct a "passed" result indicating that a gesture should be allowed.
*/
static Result passed(double confidence) {
return new Result(false, confidence, null);
}
}
}

View File

@@ -58,18 +58,19 @@ class PointerCountClassifier extends FalsingClassifier {
@Override
Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
int interactionType = getInteractionType();
if (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN) {
return new Result(mMaxPointerCount > MAX_ALLOWED_POINTERS_SWIPE_DOWN, 1);
}
return new Result(mMaxPointerCount > MAX_ALLOWED_POINTERS, 1);
int allowedPointerCount =
(interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN)
? MAX_ALLOWED_POINTERS_SWIPE_DOWN : MAX_ALLOWED_POINTERS;
return mMaxPointerCount > allowedPointerCount
? Result.falsed(1, getReason(allowedPointerCount)) : Result.passed(0);
}
@Override
String getReason() {
private String getReason(int allowedPointerCount) {
return String.format(
(Locale) null,
"{pointersObserved=%d, threshold=%d}",
mMaxPointerCount,
MAX_ALLOWED_POINTERS);
allowedPointerCount);
}
}

View File

@@ -33,7 +33,7 @@ import javax.inject.Inject;
/**
* False touch if proximity sensor is covered for more than a certain percentage of the gesture.
*
* This classifer is essentially a no-op for QUICK_SETTINGS, as we assume the sensor may be
* This classifier is essentially a no-op for QUICK_SETTINGS, as we assume the sensor may be
* covered when swiping from the top.
*/
class ProximityClassifier extends FalsingClassifier {
@@ -114,26 +114,27 @@ class ProximityClassifier extends FalsingClassifier {
@Override
Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
if (getInteractionType() == QUICK_SETTINGS) {
return new Result(false, 0);
return Result.passed(0);
}
logInfo("Percent of gesture in proximity: " + mPercentNear);
if (mPercentNear > mPercentCoveredThreshold) {
return new Result(!mDistanceClassifier.isLongSwipe(), 0.5);
Result longSwipeResult = mDistanceClassifier.isLongSwipe();
return longSwipeResult.isFalse()
? Result.falsed(0.5, getReason(longSwipeResult)) : Result.passed(0.5);
}
return new Result(false, 0.5);
return Result.passed(0.5);
}
@Override
String getReason() {
private String getReason(Result longSwipeResult) {
return String.format(
(Locale) null,
"{percentInProximity=%f, threshold=%f, distanceClassifier=%s}",
mPercentNear,
mPercentCoveredThreshold,
mDistanceClassifier.getReason());
longSwipeResult.getReason());
}
/**

View File

@@ -30,7 +30,6 @@ import javax.inject.Named;
*/
public class SingleTapClassifier extends FalsingClassifier {
private final float mTouchSlop;
private String mReason;
@Inject
SingleTapClassifier(FalsingDataProvider dataProvider,
@@ -41,35 +40,30 @@ public class SingleTapClassifier extends FalsingClassifier {
@Override
Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
return new Result(!isTap(getRecentMotionEvents()), 0.5);
return isTap(getRecentMotionEvents());
}
/** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
public boolean isTap(List<MotionEvent> motionEvents) {
public Result isTap(List<MotionEvent> motionEvents) {
float downX = motionEvents.get(0).getX();
float downY = motionEvents.get(0).getY();
for (MotionEvent event : motionEvents) {
String reason;
if (Math.abs(event.getX() - downX) >= mTouchSlop) {
mReason = "dX too big for a tap: "
reason = "dX too big for a tap: "
+ Math.abs(event.getX() - downX)
+ "vs "
+ mTouchSlop;
return false;
return Result.falsed(0.5, reason);
} else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
mReason = "dY too big for a tap: "
reason = "dY too big for a tap: "
+ Math.abs(event.getY() - downY)
+ "vs "
+ mTouchSlop;
return false;
return Result.falsed(0.5, reason);
}
}
mReason = "";
return true;
}
@Override
String getReason() {
return mReason;
return Result.passed(0);
}
}

View File

@@ -43,27 +43,35 @@ public class TypeClassifier extends FalsingClassifier {
boolean up = isUp();
boolean right = isRight();
boolean wrongDirection = true;
switch (getInteractionType()) {
case QUICK_SETTINGS:
case PULSE_EXPAND:
case NOTIFICATION_DRAG_DOWN:
return new Result(!vertical || up, 0.5);
wrongDirection = !vertical || up;
break;
case NOTIFICATION_DISMISS:
return new Result(vertical, 0.5);
wrongDirection = vertical;
break;
case UNLOCK:
case BOUNCER_UNLOCK:
return new Result(!vertical || !up, 0.5);
wrongDirection = !vertical || !up;
break;
case LEFT_AFFORDANCE: // Swiping from the bottom left corner for camera or similar.
return new Result(!right || !up, 0.5);
wrongDirection = !right || !up;
break;
case RIGHT_AFFORDANCE: // Swiping from the bottom right corner for camera or similar.
return new Result(right || !up, 0.5);
wrongDirection = right || !up;
break;
default:
return new Result(true, 1);
wrongDirection = true;
break;
}
return wrongDirection ? Result.falsed(1, getReason()) : Result.passed(0.5);
}
@Override
String getReason() {
private String getReason() {
return String.format("{vertical=%s, up=%s, right=%s}", isVertical(), isUp(), isRight());
}
}

View File

@@ -95,7 +95,7 @@ class ZigZagClassifier extends FalsingClassifier {
// For vertical lines, the difference in the y direction should be small.
if (motionEvents.size() < 3) {
return new Result(false, 0);
return Result.passed(0);
}
List<Point> rotatedPoints;
@@ -155,11 +155,11 @@ class ZigZagClassifier extends FalsingClassifier {
logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
+ "(" + maxXDeviance + "," + maxYDeviance + ")");
return new Result(devianceX > maxXDeviance || devianceY > maxYDeviance, 0.5);
return devianceX > maxXDeviance || devianceY > maxYDeviance
? Result.falsed(0.5, getReason()) : Result.passed(0.5);
}
@Override
String getReason() {
private String getReason() {
return String.format(
(Locale) null,
"{devianceX=%f, maxDevianceX=%s, devianceY=%s, maxDevianceY=%s}",

View File

@@ -72,12 +72,14 @@ public class BrightLineClassifierTest extends SysuiTestCase {
private HistoryTracker mHistoryTracker;
private FakeSystemClock mSystemClock = new FakeSystemClock();
private FalsingClassifier.Result mTrueResult = new FalsingClassifier.Result(true, 1);
private FalsingClassifier.Result mFalseResult = new FalsingClassifier.Result(false, 1);
private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, "");
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
mClassifiers.add(mClassifierA);
mClassifiers.add(mClassifierB);
when(mFalsingDataProvider.isDirty()).thenReturn(true);
@@ -111,20 +113,20 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test
public void testIsFalseTouch_ClassifierARejects() {
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(true);
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue();
}
@Test
public void testIsFalseTouch_ClassifierBRejects() {
when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(true);
when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue();
}
@Test
public void testIsFalseTouch_FaceAuth() {
// Even when the classifiers report a false, we should allow.
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(true);
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
@@ -133,7 +135,7 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test
public void testIsFalseTouch_Docked() {
// Even when the classifiers report a false, we should allow.
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(true);
when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
mDockManager.setIsDocked(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
@@ -141,36 +143,36 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test
public void testIsFalseTap_BasicCheck() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(false);
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue();
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true);
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse();
}
@Test
public void testIsFalseTap_RobustCheck_NoFaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true);
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
mFalsingDataProvider.setJustUnlockedWithFace(false);
assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue();
}
@Test
public void testIsFalseTap_RobustCheck_FaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true);
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse();
}
@Test
public void testIsFalseDoubleTap() {
when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalseResult);
when(mDoubleTapClassifier.classifyGesture()).thenReturn(mPassedResult);
assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
when(mDoubleTapClassifier.classifyGesture()).thenReturn(mTrueResult);
when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue();
}

View File

@@ -55,6 +55,9 @@ public class DoubleTapClassifierTest extends ClassifierTest {
private SingleTapClassifier mSingleTapClassifier;
private DoubleTapClassifier mClassifier;
private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, "");
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
@Before
public void setup() {
super.setup();
@@ -77,7 +80,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test
public void testSingleTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
@@ -87,7 +90,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test
public void testDoubleTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -97,13 +100,13 @@ public class DoubleTapClassifierTest extends ClassifierTest {
addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP, TOUCH_SLOP);
addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP);
boolean result = mClassifier.classifyGesture().isFalse();
assertThat(mClassifier.getReason(), result, is(false));
FalsingClassifier.Result result = mClassifier.classifyGesture();
assertThat(result.getReason(), result.isFalse(), is(false));
}
@Test
public void testBadFirstTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(false, true);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult, mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -119,7 +122,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test
public void testBadSecondTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(true, false);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult, mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -135,7 +138,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test
public void testBadTouchSlop() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -151,7 +154,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test
public void testBadTouchSlow() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(true);
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);

View File

@@ -120,6 +120,9 @@ public class HistoryTrackerTest extends SysuiTestCase {
private void addResult(boolean falsed, double confidence) {
mHistoryTracker.addResults(Collections.singletonList(
new FalsingClassifier.Result(falsed, confidence)), mSystemClock.uptimeMillis());
falsed
? FalsingClassifier.Result.falsed(confidence, "test")
: FalsingClassifier.Result.passed(confidence)),
mSystemClock.uptimeMillis());
}
}

View File

@@ -50,12 +50,16 @@ public class ProximityClassifierTest extends ClassifierTest {
private DistanceClassifier mDistanceClassifier;
private FalsingClassifier mClassifier;
private final FalsingClassifier.Result mFalsedResult =
FalsingClassifier.Result.falsed(1, "test");
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
@Before
public void setup() {
super.setup();
MockitoAnnotations.initMocks(this);
when(mDataProvider.getInteractionType()).thenReturn(GENERIC);
when(mDistanceClassifier.isLongSwipe()).thenReturn(false);
when(mDistanceClassifier.isLongSwipe()).thenReturn(mFalsedResult);
mClassifier = new ProximityClassifier(
mDistanceClassifier, mDataProvider, new DeviceConfigProxyFake());
}
@@ -117,7 +121,7 @@ public class ProximityClassifierTest extends ClassifierTest {
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
when(mDistanceClassifier.isLongSwipe()).thenReturn(true);
when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult);
assertThat(mClassifier.classifyGesture().isFalse(), is(false));
}

View File

@@ -142,12 +142,12 @@ public class SingleTapClassifierTest extends ClassifierTest {
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
assertThat(mClassifier.isTap(mMotionEvents), is(true));
assertThat(mClassifier.isTap(mMotionEvents).isFalse(), is(false));
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
assertThat(mClassifier.isTap(mMotionEvents), is(false));
assertThat(mClassifier.isTap(mMotionEvents).isFalse(), is(true));
}