• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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