• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.incallui.answer.impl.classifier;
18 
19 import android.content.Context;
20 import android.hardware.SensorEvent;
21 import android.util.DisplayMetrics;
22 import android.view.MotionEvent;
23 import com.android.dialer.configprovider.ConfigProviderComponent;
24 
25 /** An classifier trying to determine whether it is a human interacting with the phone or not. */
26 class HumanInteractionClassifier extends Classifier {
27 
28   private static final String CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED =
29       "answer_false_touch_detection_enabled";
30 
31   private final StrokeClassifier[] strokeClassifiers;
32   private final GestureClassifier[] gestureClassifiers;
33   private final HistoryEvaluator historyEvaluator;
34   private final boolean enabled;
35 
HumanInteractionClassifier(Context context)36   HumanInteractionClassifier(Context context) {
37     DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
38 
39     // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
40     // were to be used separately. Due negligible differences in xdpi and ydpi we can just
41     // take the average.
42     // Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling.
43     float dpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
44     classifierData = new ClassifierData(dpi, displayMetrics.heightPixels);
45     historyEvaluator = new HistoryEvaluator();
46     enabled =
47         ConfigProviderComponent.get(context)
48             .getConfigProvider()
49             .getBoolean(CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED, true);
50 
51     strokeClassifiers =
52         new StrokeClassifier[] {
53           new AnglesClassifier(classifierData),
54           new SpeedClassifier(classifierData),
55           new DurationCountClassifier(classifierData),
56           new EndPointRatioClassifier(classifierData),
57           new EndPointLengthClassifier(classifierData),
58           new AccelerationClassifier(classifierData),
59           new SpeedAnglesClassifier(classifierData),
60           new LengthCountClassifier(classifierData),
61           new DirectionClassifier(classifierData)
62         };
63 
64     gestureClassifiers =
65         new GestureClassifier[] {
66           new PointerCountClassifier(classifierData), new ProximityClassifier(classifierData)
67         };
68   }
69 
70   @Override
onTouchEvent(MotionEvent event)71   public void onTouchEvent(MotionEvent event) {
72 
73     // If the user is dragging down the notification, they might want to drag it down
74     // enough to see the content, read it for a while and then lift the finger to open
75     // the notification. This kind of motion scores very bad in the Classifier so the
76     // MotionEvents which are close to the current position of the finger are not
77     // sent to the classifiers until the finger moves far enough. When the finger if lifted
78     // up, the last MotionEvent which was far enough from the finger is set as the final
79     // MotionEvent and sent to the Classifiers.
80     addTouchEvent(event);
81   }
82 
addTouchEvent(MotionEvent event)83   private void addTouchEvent(MotionEvent event) {
84     classifierData.update(event);
85 
86     for (StrokeClassifier c : strokeClassifiers) {
87       c.onTouchEvent(event);
88     }
89 
90     for (GestureClassifier c : gestureClassifiers) {
91       c.onTouchEvent(event);
92     }
93 
94     int size = classifierData.getEndingStrokes().size();
95     for (int i = 0; i < size; i++) {
96       Stroke stroke = classifierData.getEndingStrokes().get(i);
97       float evaluation = 0.0f;
98       for (StrokeClassifier c : strokeClassifiers) {
99         float e = c.getFalseTouchEvaluation(stroke);
100         evaluation += e;
101       }
102 
103       historyEvaluator.addStroke(evaluation);
104     }
105 
106     int action = event.getActionMasked();
107     if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
108       float evaluation = 0.0f;
109       for (GestureClassifier c : gestureClassifiers) {
110         float e = c.getFalseTouchEvaluation();
111         evaluation += e;
112       }
113       historyEvaluator.addGesture(evaluation);
114     }
115 
116     classifierData.cleanUp(event);
117   }
118 
119   @Override
onSensorChanged(SensorEvent event)120   public void onSensorChanged(SensorEvent event) {
121     for (Classifier c : strokeClassifiers) {
122       c.onSensorChanged(event);
123     }
124 
125     for (Classifier c : gestureClassifiers) {
126       c.onSensorChanged(event);
127     }
128   }
129 
isFalseTouch()130   boolean isFalseTouch() {
131     float evaluation = historyEvaluator.getEvaluation();
132     return evaluation >= 5.0f;
133   }
134 
isEnabled()135   public boolean isEnabled() {
136     return enabled;
137   }
138 
139   @Override
getTag()140   public String getTag() {
141     return "HIC";
142   }
143 }
144