Loading packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +12 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); } Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java +6 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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}", Loading packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java +4 −4 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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() { Loading packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java +10 −15 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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; } Loading Loading @@ -106,9 +106,4 @@ public class DoubleTapClassifier extends FalsingClassifier { return true; } @Override String getReason() { return mReason.toString(); } } packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java +43 −10 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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() { Loading @@ -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); } } } Loading
packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +12 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); } Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java +6 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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}", Loading
packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java +4 −4 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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() { Loading
packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java +10 −15 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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; } Loading Loading @@ -106,9 +106,4 @@ public class DoubleTapClassifier extends FalsingClassifier { return true; } @Override String getReason() { return mReason.toString(); } }
packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java +43 −10 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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() { Loading @@ -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); } } }