• 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.common.ConfigProviderBindings;
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[] mStrokeClassifiers;
32   private final GestureClassifier[] mGestureClassifiers;
33   private final HistoryEvaluator mHistoryEvaluator;
34   private final boolean mEnabled;
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     mClassifierData = new ClassifierData(dpi, displayMetrics.heightPixels);
45     mHistoryEvaluator = new HistoryEvaluator();
46     mEnabled =
47         ConfigProviderBindings.get(context)
48             .getBoolean(CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED, true);
49 
50     mStrokeClassifiers =
51         new StrokeClassifier[] {
52           new AnglesClassifier(mClassifierData),
53           new SpeedClassifier(mClassifierData),
54           new DurationCountClassifier(mClassifierData),
55           new EndPointRatioClassifier(mClassifierData),
56           new EndPointLengthClassifier(mClassifierData),
57           new AccelerationClassifier(mClassifierData),
58           new SpeedAnglesClassifier(mClassifierData),
59           new LengthCountClassifier(mClassifierData),
60           new DirectionClassifier(mClassifierData)
61         };
62 
63     mGestureClassifiers =
64         new GestureClassifier[] {
65           new PointerCountClassifier(mClassifierData), new ProximityClassifier(mClassifierData)
66         };
67   }
68 
69   @Override
onTouchEvent(MotionEvent event)70   public void onTouchEvent(MotionEvent event) {
71 
72     // If the user is dragging down the notification, they might want to drag it down
73     // enough to see the content, read it for a while and then lift the finger to open
74     // the notification. This kind of motion scores very bad in the Classifier so the
75     // MotionEvents which are close to the current position of the finger are not
76     // sent to the classifiers until the finger moves far enough. When the finger if lifted
77     // up, the last MotionEvent which was far enough from the finger is set as the final
78     // MotionEvent and sent to the Classifiers.
79     addTouchEvent(event);
80   }
81 
addTouchEvent(MotionEvent event)82   private void addTouchEvent(MotionEvent event) {
83     mClassifierData.update(event);
84 
85     for (StrokeClassifier c : mStrokeClassifiers) {
86       c.onTouchEvent(event);
87     }
88 
89     for (GestureClassifier c : mGestureClassifiers) {
90       c.onTouchEvent(event);
91     }
92 
93     int size = mClassifierData.getEndingStrokes().size();
94     for (int i = 0; i < size; i++) {
95       Stroke stroke = mClassifierData.getEndingStrokes().get(i);
96       float evaluation = 0.0f;
97       for (StrokeClassifier c : mStrokeClassifiers) {
98         float e = c.getFalseTouchEvaluation(stroke);
99         evaluation += e;
100       }
101 
102       mHistoryEvaluator.addStroke(evaluation);
103     }
104 
105     int action = event.getActionMasked();
106     if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
107       float evaluation = 0.0f;
108       for (GestureClassifier c : mGestureClassifiers) {
109         float e = c.getFalseTouchEvaluation();
110         evaluation += e;
111       }
112       mHistoryEvaluator.addGesture(evaluation);
113     }
114 
115     mClassifierData.cleanUp(event);
116   }
117 
118   @Override
onSensorChanged(SensorEvent event)119   public void onSensorChanged(SensorEvent event) {
120     for (Classifier c : mStrokeClassifiers) {
121       c.onSensorChanged(event);
122     }
123 
124     for (Classifier c : mGestureClassifiers) {
125       c.onSensorChanged(event);
126     }
127   }
128 
isFalseTouch()129   boolean isFalseTouch() {
130     float evaluation = mHistoryEvaluator.getEvaluation();
131     return evaluation >= 5.0f;
132   }
133 
isEnabled()134   public boolean isEnabled() {
135     return mEnabled;
136   }
137 
138   @Override
getTag()139   public String getTag() {
140     return "HIC";
141   }
142 }
143