1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.classifier; 18 19 import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS; 20 import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TOUCH_SLOP; 21 22 import android.view.MotionEvent; 23 24 import java.util.List; 25 26 import javax.inject.Inject; 27 import javax.inject.Named; 28 29 /** 30 * Returns a false touch if the most two recent gestures are not taps or are too far apart. 31 */ 32 public class DoubleTapClassifier extends FalsingClassifier { 33 34 private final SingleTapClassifier mSingleTapClassifier; 35 private final float mDoubleTapSlop; 36 private final long mDoubleTapTimeMs; 37 38 @Inject DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier, @Named(DOUBLE_TAP_TOUCH_SLOP) float doubleTapSlop, @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs)39 DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier, 40 @Named(DOUBLE_TAP_TOUCH_SLOP) float doubleTapSlop, 41 @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) { 42 super(dataProvider); 43 mSingleTapClassifier = singleTapClassifier; 44 mDoubleTapSlop = doubleTapSlop; 45 mDoubleTapTimeMs = doubleTapTimeMs; 46 } 47 48 @Override calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)49 Result calculateFalsingResult( 50 @Classifier.InteractionType int interactionType, 51 double historyBelief, double historyConfidence) { 52 List<MotionEvent> secondTapEvents = getRecentMotionEvents(); 53 List<MotionEvent> firstTapEvents = getPriorMotionEvents(); 54 55 StringBuilder reason = new StringBuilder(); 56 57 if (firstTapEvents == null) { 58 return falsed(0, "Only one gesture recorded"); 59 } 60 61 return !isDoubleTap(firstTapEvents, secondTapEvents, reason) 62 ? falsed(0.5, reason.toString()) : Result.passed(0.5); 63 } 64 65 /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */ isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents, StringBuilder reason)66 public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents, 67 StringBuilder reason) { 68 69 Result firstTap = mSingleTapClassifier.isTap(firstEvents, 0.5); 70 if (firstTap.isFalse()) { 71 reason.append("First gesture is not a tap. ").append(firstTap.getReason()); 72 return false; 73 } 74 75 Result secondTap = mSingleTapClassifier.isTap(secondEvents, 0.5); 76 if (secondTap.isFalse()) { 77 reason.append("Second gesture is not a tap. ").append(secondTap.getReason()); 78 return false; 79 } 80 81 MotionEvent firstFinalEvent = firstEvents.get(firstEvents.size() - 1); 82 MotionEvent secondFinalEvent = secondEvents.get(secondEvents.size() - 1); 83 84 long dt = secondFinalEvent.getEventTime() - firstFinalEvent.getEventTime(); 85 86 if (dt > mDoubleTapTimeMs) { 87 reason.append("Time between taps too large: ").append(dt).append("ms"); 88 return false; 89 } 90 91 if (Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX()) >= mDoubleTapSlop) { 92 reason.append("Delta X between taps too large:") 93 .append(Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX())) 94 .append(" vs ") 95 .append(mDoubleTapSlop); 96 return false; 97 } 98 99 if (Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY()) >= mDoubleTapSlop) { 100 reason.append("Delta Y between taps too large:") 101 .append(Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY())) 102 .append(" vs ") 103 .append(mDoubleTapSlop); 104 return false; 105 } 106 107 return true; 108 } 109 } 110