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

Commit 1179f84f authored by Dave Mankoff's avatar Dave Mankoff
Browse files

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
parent 3ae5052e
Loading
Loading
Loading
Loading
+12 −10
Original line number Diff line number Diff line
@@ -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
+6 −6
Original line number Diff line number Diff line
@@ -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}",
+4 −4
Original line number Diff line number Diff line
@@ -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() {
+10 −15
Original line number Diff line number Diff line
@@ -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();
    }
}
+43 −10
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
Loading