• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.view.MotionEvent;
20 
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 
25 /**
26  * A classifier which for each point from a stroke, it creates a point on plane with coordinates
27  * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
28  * and then it calculates the angle variance of these points like the class
29  * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores
30  * the last point of a stroke because the UP event comes in with some delay and this ruins the
31  * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
32  * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
33  * does that is because the speed of a good stroke is most often increases, so most of these angels
34  * should be in this interval.
35  */
36 public class SpeedAnglesClassifier extends StrokeClassifier {
37     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
38 
SpeedAnglesClassifier(ClassifierData classifierData)39     public SpeedAnglesClassifier(ClassifierData classifierData) {
40         mClassifierData = classifierData;
41     }
42 
43     @Override
getTag()44     public String getTag() {
45         return "SPD_ANG";
46     }
47 
48     @Override
onTouchEvent(MotionEvent event)49     public void onTouchEvent(MotionEvent event) {
50         int action = event.getActionMasked();
51 
52         if (action == MotionEvent.ACTION_DOWN) {
53             mStrokeMap.clear();
54         }
55 
56         for (int i = 0; i < event.getPointerCount(); i++) {
57             Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
58 
59             if (mStrokeMap.get(stroke) == null) {
60                 mStrokeMap.put(stroke, new Data());
61             }
62 
63             if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
64                     && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
65                 mStrokeMap.get(stroke).addPoint(
66                         stroke.getPoints().get(stroke.getPoints().size() - 1));
67             }
68         }
69     }
70 
71     @Override
getFalseTouchEvaluation(int type, Stroke stroke)72     public float getFalseTouchEvaluation(int type, Stroke stroke) {
73         Data data = mStrokeMap.get(stroke);
74         return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
75                 + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
76     }
77 
78     private static class Data {
79         private final float DURATION_SCALE = 1e8f;
80         private final float LENGTH_SCALE = 1.0f;
81         private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
82 
83         private List<Point> mLastThreePoints = new ArrayList<>();
84         private Point mPreviousPoint;
85         private float mPreviousAngle;
86         private float mSumSquares;
87         private float mSum;
88         private float mCount;
89         private float mDist;
90         private float mAnglesCount;
91         private float mAcceleratingAngles;
92 
Data()93         public Data() {
94             mPreviousPoint = null;
95             mPreviousAngle = (float) Math.PI;
96             mSumSquares = 0.0f;
97             mSum = 0.0f;
98             mCount = 1.0f;
99             mDist = 0.0f;
100             mAnglesCount = mAcceleratingAngles = 0.0f;
101         }
102 
addPoint(Point point)103         public void addPoint(Point point) {
104             if (mPreviousPoint != null) {
105                 mDist += mPreviousPoint.dist(point);
106             }
107 
108             mPreviousPoint = point;
109             Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
110                     mDist / LENGTH_SCALE);
111 
112             // Checking if the added point is different than the previously added point
113             // Repetitions are being ignored so that proper angles are calculated.
114             if (mLastThreePoints.isEmpty()
115                     || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
116                 mLastThreePoints.add(speedPoint);
117                 if (mLastThreePoints.size() == 4) {
118                     mLastThreePoints.remove(0);
119 
120                     float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
121                             mLastThreePoints.get(2));
122 
123                     mAnglesCount++;
124                     if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
125                         mAcceleratingAngles++;
126                     }
127 
128                     float difference = angle - mPreviousAngle;
129                     mSum += difference;
130                     mSumSquares += difference * difference;
131                     mCount += 1.0;
132                     mPreviousAngle = angle;
133                 }
134             }
135         }
136 
getAnglesVariance()137         public float getAnglesVariance() {
138             return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
139         }
140 
getAnglesPercentage()141         public float getAnglesPercentage() {
142             if (mAnglesCount == 0.0f) {
143                 return 1.0f;
144             }
145             return (mAcceleratingAngles) / mAnglesCount;
146         }
147     }
148 }