added Classifiers and Evaluators

Change-Id: I9054c30cde665546de453e64572da619b1f6a799
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
new file mode 100644
index 0000000..86bea87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.util.HashMap;
+
+/**
+ * A classifier which looks at the speed and distance between successive points of a Stroke.
+ * It looks at two consecutive speeds between two points and calculates the ratio between them.
+ * The final result is the maximum of these values. It does the same for distances. If some speed
+ * or distance is equal to zero then the ratio between this and the next part is not calculated. To
+ * the duration of each part there is added one nanosecond so that it is always possible to
+ * calculate the speed of a part.
+ */
+public class AccelerationClassifier extends StrokeClassifier {
+    private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+    public AccelerationClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mStrokeMap.clear();
+        }
+
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+            Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
+            if (mStrokeMap.get(stroke) == null) {
+                mStrokeMap.put(stroke, new Data(point));
+            } else {
+                mStrokeMap.get(stroke).addPoint(point);
+            }
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        Data data = mStrokeMap.get(stroke);
+        return SpeedRatioEvaluator.evaluate(data.maxSpeedRatio)
+                + DistanceRatioEvaluator.evaluate(data.maxDistanceRatio);
+    }
+
+    private static class Data {
+        public Point previousPoint;
+        public float previousSpeed;
+        public float previousDistance;
+        public float maxSpeedRatio;
+        public float maxDistanceRatio;
+
+        public Data(Point point) {
+            previousPoint = point;
+            previousSpeed = previousDistance = 0.0f;
+            maxDistanceRatio = maxSpeedRatio = 0.0f;
+        }
+
+        public void addPoint(Point point) {
+            float distance = previousPoint.dist(point);
+            float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
+            float speed = distance / duration;
+            if (previousDistance != 0.0f) {
+                maxDistanceRatio = Math.max(maxDistanceRatio, distance / previousDistance);
+            }
+
+            if (previousSpeed != 0.0f) {
+                maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
+            }
+
+            previousDistance = distance;
+            previousSpeed = speed;
+            previousPoint = point;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
index 8c681fc..c74339b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
@@ -29,10 +29,15 @@
  * For each stroke it keeps its last three points. If some successive points are the same, it
  * ignores the repetitions. If a new point is added, the classifier calculates the angle between
  * the last three points. After that, it calculates the difference between this angle and the
- * previously calculated angle. The return value of the classifier is the variance of the
- * differences from a stroke. To the differences there is artificially added value 0.0 and the
- * difference between the first angle and PI (angles are in radians). It helps with strokes which
- * have few points and punishes more strokes which are not smooth.
+ * previously calculated angle. Then it calculates the variance of the differences from a stroke.
+ * To the differences there is artificially added value 0.0 and the difference between the first
+ * angle and PI (angles are in radians). It helps with strokes which have few points and punishes
+ * more strokes which are not smooth. This classifier also tries to split the stroke into two parts
+ * int the place in which the biggest angle is. It calculates the angle variance of the two parts
+ * and sums them up. The reason the classifier is doing this, is because some human swipes at the
+ * beginning go for a moment in one direction and then they rapidly change direction for the rest
+ * of the stroke (like a tick). The final result is the minimum of angle variance of the whole
+ * stroke and the sum of angle variances of the two parts split up.
  */
 public class AnglesVarianceClassifier extends StrokeClassifier {
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
@@ -61,21 +66,28 @@
 
     @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return mStrokeMap.get(stroke).getAnglesVariance();
+        return AnglesVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
     }
 
-    private class Data {
+    private static class Data {
         private List<Point> mLastThreePoints = new ArrayList<>();
+        private float mFirstAngleVariance;
         private float mPreviousAngle;
+        private float mBiggestAngle;
         private float mSumSquares;
+        private float mSecondSumSquares;
         private float mSum;
+        private float mSecondSum;
         private float mCount;
+        private float mSecondCount;
 
         public Data() {
+            mFirstAngleVariance = 0.0f;
             mPreviousAngle = (float) Math.PI;
-            mSumSquares = 0.0f;
-            mSum = 0.0f;
-            mCount = 1.0f;
+            mBiggestAngle = 0.0f;
+            mSumSquares = mSecondSumSquares = 0.0f;
+            mSum = mSecondSum = 0.0f;
+            mCount = mSecondCount = 1.0f;
         }
 
         public void addPoint(Point point) {
@@ -87,10 +99,26 @@
                 if (mLastThreePoints.size() == 4) {
                     mLastThreePoints.remove(0);
 
-                    float angle = getAngle(mLastThreePoints.get(0), mLastThreePoints.get(1),
+                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
                             mLastThreePoints.get(2));
 
                     float difference = angle - mPreviousAngle;
+
+                    // If this is the biggest angle of the stroke so then we save the value of
+                    // the angle variance so far and start to count the values for the angle
+                    // variance of the second part.
+                    if (mBiggestAngle < angle) {
+                        mBiggestAngle = angle;
+                        mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+                        mSecondSumSquares = 0.0f;
+                        mSecondSum = 0.0f;
+                        mSecondCount = 1.0f;
+                    } else {
+                        mSecondSum += difference;
+                        mSecondSumSquares += difference * difference;
+                        mSecondCount += 1.0;
+                    }
+
                     mSum += difference;
                     mSumSquares += difference * difference;
                     mCount += 1.0;
@@ -99,21 +127,14 @@
             }
         }
 
-        private float getAngle(Point a, Point b, Point c) {
-            float dist1 = a.dist(b);
-            float dist2 = b.dist(c);
-            float crossProduct = b.crossProduct(a, c);
-            float dotProduct = b.dotProduct(a, c);
-            float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
-            float angle = (float) Math.acos(cos);
-            if (crossProduct < 0.0) {
-                angle = 2.0f * (float) Math.PI - angle;
-            }
-            return angle;
+        public float getAnglesVariance(float sumSquares, float sum, float count) {
+            return sumSquares / count - (sum / count) * (sum / count);
         }
 
         public float getAnglesVariance() {
-            return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+            return Math.min(getAnglesVariance(mSumSquares, mSum, mCount),
+                    mFirstAngleVariance + getAnglesVariance(mSecondSumSquares, mSecondSum,
+                            mSecondCount));
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
new file mode 100644
index 0000000..99cc1a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class AnglesVarianceEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 0.05) evaluation++;
+        if (value > 0.10) evaluation++;
+        if (value > 0.20) evaluation++;
+        if (value > 0.40) evaluation++;
+        if (value > 0.80) evaluation++;
+        if (value > 1.50) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
index bccad4e..649279d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
@@ -28,8 +28,12 @@
 public class ClassifierData {
     private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
     private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
+    private float mXdpi;
+    private float mYdpi;
 
-    public ClassifierData() {
+    public ClassifierData(float xdpi, float ydpi) {
+        mXdpi = xdpi;
+        mYdpi = ydpi;
     }
 
     public void update(MotionEvent event) {
@@ -42,7 +46,7 @@
         for (int i = 0; i < event.getPointerCount(); i++) {
             int id = event.getPointerId(i);
             if (mCurrentStrokes.get(id) == null) {
-                mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano()));
+                mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mXdpi, mYdpi));
             }
             mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
                     event.getEventTimeNano());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
new file mode 100644
index 0000000..8acb009
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class DistanceRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value <= 1.0) evaluation++;
+        if (value <= 0.5) evaluation++;
+        if (value > 4.0) evaluation++;
+        if (value > 7.0) evaluation++;
+        if (value > 14.0) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
new file mode 100644
index 0000000..8924694
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+public class DurationCountClassifier extends StrokeClassifier {
+    public DurationCountClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
new file mode 100644
index 0000000..5395983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+
+public class DurationCountEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.0105) evaluation++;
+        if (value < 0.00909) evaluation++;
+        if (value < 0.00667) evaluation++;
+        if (value > 0.0333) evaluation++;
+        if (value > 0.0500) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
new file mode 100644
index 0000000..78bc0dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the distance between the first and the last point from the stroke.
+ */
+public class EndPointLengthClassifier extends StrokeClassifier {
+    public EndPointLengthClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
new file mode 100644
index 0000000..bb2f1c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class EndPointLengthEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.05) evaluation += 2.0;
+        if (value < 0.1) evaluation += 2.0;
+        if (value < 0.2) evaluation += 2.0;
+        if (value < 0.3) evaluation += 2.0;
+        if (value < 0.4) evaluation += 2.0;
+        if (value < 0.5) evaluation += 2.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
new file mode 100644
index 0000000..c125e00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the total length covered by the stroke and the
+ * distance between the first and last point from this stroke.
+ */
+public class EndPointRatioClassifier extends StrokeClassifier {
+    public EndPointRatioClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        if (stroke.getTotalLength() == 0.0f) {
+            return 1.0f;
+        }
+        return EndPointRatioEvaluator.evaluate(
+                stroke.getEndPointLength() / stroke.getTotalLength());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
new file mode 100644
index 0000000..529fcec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class EndPointRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.85) evaluation++;
+        if (value < 0.75) evaluation++;
+        if (value < 0.65) evaluation++;
+        if (value < 0.55) evaluation++;
+        if (value < 0.45) evaluation++;
+        if (value < 0.35) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
index e7f4c35..11388fc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
@@ -25,7 +25,7 @@
     /**
      * @param type the type of action for which this method is called
      * @return a non-negative value which is used to determine whether the most recent gesture is a
-     *         false interaction. The bigger the value the greater the chance that this a false
+     *         false interaction; the bigger the value the greater the chance that this a false
      *         interaction.
      */
     public abstract float getFalseTouchEvaluation(int type);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
index b057bda..85a9bee 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
@@ -100,7 +100,7 @@
      * For each stroke it holds its initial value and the current weight. Initially the
      * weight is set to 1.0
      */
-    private class Data {
+    private static class Data {
         public float evaluation;
         public float weight;
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 86ea640..6ef805c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -55,10 +55,21 @@
 
     private HumanInteractionClassifier(Context context) {
         mContext = context;
-        mClassifierData = new ClassifierData();
+        mClassifierData = new ClassifierData(mContext.getResources().getDisplayMetrics().xdpi,
+                mContext.getResources().getDisplayMetrics().ydpi);
         mHistoryEvaluator = new HistoryEvaluator();
 
         mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData));
+        mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
+        mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
+        mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
+        mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
+        mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
+        mStrokeClassifiers.add(new SpeedVarianceClassifier(mClassifierData));
+        mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
+
+        mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
+        mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
 
         mStrokeClassifiersSize = mStrokeClassifiers.size();
         mGestureClassifiersSize = mGestureClassifiers.size();
@@ -128,6 +139,13 @@
 
     @Override
     public void onSensorChanged(SensorEvent event) {
+        for (int i = 0; i < mStrokeClassifiers.size(); i++) {
+            mStrokeClassifiers.get(i).onSensorChanged(event);
+        }
+
+        for (int i = 0; i < mGestureClassifiers.size(); i++) {
+            mGestureClassifiers.get(i).onSensorChanged(event);
+        }
     }
 
     public boolean isFalseTouch() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
new file mode 100644
index 0000000..1ea467b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+public class LengthCountClassifier extends StrokeClassifier {
+    public LengthCountClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return LengthCountEvaluator.evaluate(stroke.getTotalLength() / stroke.getCount());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
new file mode 100644
index 0000000..68f163d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points.
+ */
+public class LengthCountEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.07) evaluation++;
+        if (value < 0.05) evaluation++;
+        if (value < 0.02) evaluation++;
+        if (value > 0.6) evaluation++;
+        if (value > 1.2) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Point.java b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
index e7dbae1..f3dc2be 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Point.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
@@ -56,4 +56,28 @@
     public float dotProduct(Point a, Point b) {
         return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
     }
+
+    /**
+     * Calculates the angle in radians created by points (a, this, b). If any two of these points
+     * are the same, the method will return 0.0f
+     *
+     * @return the angle in radians
+     */
+    public float getAngle(Point a, Point b) {
+        float dist1 = dist(a);
+        float dist2 = dist(b);
+
+        if (dist1 == 0.0f || dist2 == 0.0f) {
+            return 0.0f;
+        }
+
+        float crossProduct = crossProduct(a, b);
+        float dotProduct = dotProduct(a, b);
+        float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
+        float angle = (float) Math.acos(cos);
+        if (crossProduct < 0.0) {
+            angle = 2.0f * (float) Math.PI - angle;
+        }
+        return angle;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
new file mode 100644
index 0000000..5097b63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the total number of traces in the whole gesture.
+ */
+public class PointerCountClassifier extends GestureClassifier {
+    private int mCount;
+
+    public PointerCountClassifier(ClassifierData classifierData) {
+        mCount = 0;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mCount = 1;
+        }
+
+        if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            ++mCount;
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type) {
+        return PointerCountEvaluator.evaluate(mCount);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
new file mode 100644
index 0000000..d7a3af0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class PointerCountEvaluator {
+    public static float evaluate(int value) {
+        return (value - 1) * (value - 1);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
new file mode 100644
index 0000000..6995064
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
+ * the proximity sensor showing the near state during the whole gesture
+ */
+public class ProximityClassifier extends GestureClassifier {
+    private long mGestureStartTimeNano;
+    private long mNearStartTimeNano;
+    private long mNearDuration;
+    private boolean mNear;
+    private float mAverageNear;
+
+    public ProximityClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+            update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
+        }
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mGestureStartTimeNano = event.getEventTimeNano();
+            mNearStartTimeNano = event.getEventTimeNano();
+            mNearDuration = 0;
+        }
+
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            update(mNear, event.getEventTimeNano());
+            long duration = event.getEventTimeNano() - mGestureStartTimeNano;
+
+            if (duration == 0) {
+                mAverageNear = mNear ? 1.0f : 0.0f;
+            } else {
+                mAverageNear = (float) mNearDuration / (float) duration;
+            }
+        }
+    }
+
+
+    /**
+     * @param near is the sensor showing the near state right now
+     * @param timestampNano time of this event in nanoseconds
+     */
+    private void update(boolean near, long timestampNano) {
+        // This if is necessary because MotionEvents and SensorEvents do not come in
+        // chronological order
+        if (timestampNano > mNearStartTimeNano) {
+            // if the state before was near then add the difference of the current time and
+            // mNearStartTimeNano to mNearDuration.
+            if (mNear) {
+                mNearDuration += timestampNano - mNearStartTimeNano;
+            }
+
+            // if the new state is near, set mNearStartTimeNano equal to this moment.
+            if (near) {
+                mNearStartTimeNano = timestampNano;
+            }
+        }
+        mNear = near;
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type) {
+        return ProximityEvaluator.evaluate(mAverageNear, type);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
new file mode 100644
index 0000000..91002bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class ProximityEvaluator {
+    public static float evaluate(float value, int type) {
+        float evaluation = 0.0f;
+        float threshold = 0.1f;
+        if (type == Classifier.QUICK_SETTINGS) {
+            threshold = 1.0f;
+        }
+        if (value >= threshold) evaluation += 2.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
new file mode 100644
index 0000000..81b78c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+/**
+ * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in
+ * inches per second.
+ */
+public class SpeedClassifier extends StrokeClassifier {
+    private final float NANOS_TO_SECONDS = 1e9f;
+
+    public SpeedClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
+        if (duration == 0.0f) {
+            return SpeedEvaluator.evaluate(0.0f);
+        }
+        return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
new file mode 100644
index 0000000..c0e4a2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 4.0 || value > 35.0) evaluation += 1.0;
+        if (value < 2.2) evaluation += 1.0;
+        if (value > 50.0) evaluation += 1.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
new file mode 100644
index 0000000..349aa9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 9.0) ++evaluation;
+        if (value > 18.0) ++evaluation;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
new file mode 100644
index 0000000..9a30fe1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+import android.view.MotionEvent;
+
+import java.lang.Math;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A classifier which for each point from a stroke, it creates a point on plane with coordinates
+ * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
+ * and then it calculates the angle variance of these points like the class
+ * {@link AnglesVarianceClassifier} (without splitting it into two parts). The classifier ignores
+ * the last point of a stroke because the UP event comes in with some delay and this ruins the
+ * smoothness of this curve
+ */
+public class SpeedVarianceClassifier extends StrokeClassifier {
+    private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+    public SpeedVarianceClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mStrokeMap.clear();
+        }
+
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+            if (mStrokeMap.get(stroke) == null) {
+                mStrokeMap.put(stroke, new Data());
+            }
+
+            if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
+                    && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+                mStrokeMap.get(stroke).addPoint(
+                        stroke.getPoints().get(stroke.getPoints().size() - 1));
+            }
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return SpeedVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+    }
+
+    private static class Data {
+        private final float DURATION_SCALE = 1e8f;
+        private final float LENGTH_SCALE = 1.0f;
+
+        private List<Point> mLastThreePoints = new ArrayList<>();
+        private Point mPreviousPoint;
+        private float mPreviousAngle;
+        private float mSumSquares;
+        private float mSum;
+        private float mCount;
+        private float mDist;
+
+        public Data() {
+            mPreviousPoint = null;
+            mPreviousAngle = (float) Math.PI;
+            mSumSquares = 0.0f;
+            mSum = 0.0f;
+            mCount = 1.0f;
+            mDist = 0.0f;
+        }
+
+        public void addPoint(Point point) {
+            if (mPreviousPoint != null) {
+                mDist += mPreviousPoint.dist(point);
+            }
+
+            mPreviousPoint = point;
+            Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
+                    mDist / LENGTH_SCALE);
+
+            // Checking if the added point is different than the previously added point
+            // Repetitions are being ignored so that proper angles are calculated.
+            if (mLastThreePoints.isEmpty()
+                    || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
+                mLastThreePoints.add(speedPoint);
+                if (mLastThreePoints.size() == 4) {
+                    mLastThreePoints.remove(0);
+
+                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
+                            mLastThreePoints.get(2));
+
+                    float difference = angle - mPreviousAngle;
+                    mSum += difference;
+                    mSumSquares += difference * difference;
+                    mCount += 1.0;
+                    mPreviousAngle = angle;
+                }
+            }
+        }
+
+        public float getAnglesVariance() {
+            return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
new file mode 100644
index 0000000..8f9a7e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.classifier;
+
+public class SpeedVarianceEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 0.06) evaluation += 1.0;
+        if (value > 0.15) evaluation += 1.0;
+        if (value > 0.3) evaluation += 1.0;
+        if (value > 0.6) evaluation += 1.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
index 8c3fdd4..49e6fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
@@ -23,17 +23,48 @@
  * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
  */
 public class Stroke {
+    private final float NANOS_TO_SECONDS = 1e9f;
+
     private ArrayList<Point> mPoints = new ArrayList<>();
     private long mStartTimeNano;
     private long mEndTimeNano;
+    private float mLength;
+    private float mXdpi;
+    private float mYdpi;
 
-    public Stroke(long eventTimeNano) {
+    public Stroke(long eventTimeNano, float xdpi, float ydpi) {
+        mXdpi = xdpi;
+        mYdpi = ydpi;
         mStartTimeNano = mEndTimeNano = eventTimeNano;
     }
 
     public void addPoint(float x, float y, long eventTimeNano) {
         mEndTimeNano = eventTimeNano;
-        mPoints.add(new Point(x, y, eventTimeNano - mStartTimeNano));
+        Point point = new Point(x / mXdpi, y / mYdpi, eventTimeNano - mStartTimeNano);
+        if (!mPoints.isEmpty()) {
+            mLength += mPoints.get(mPoints.size() - 1).dist(point);
+        }
+        mPoints.add(point);
+    }
+
+    public int getCount() {
+        return mPoints.size();
+    }
+
+    public float getTotalLength() {
+        return mLength;
+    }
+
+    public float getEndPointLength() {
+        return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
+    }
+
+    public long getDurationNanos() {
+        return mEndTimeNano - mStartTimeNano;
+    }
+
+    public float getDurationSeconds() {
+        return (float) getDurationNanos() / NANOS_TO_SECONDS;
     }
 
     public ArrayList<Point> getPoints() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
index d561f46..5da392f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
@@ -24,8 +24,8 @@
     /**
      * @param type the type of action for which this method is called
      * @param stroke the stroke for which the evaluation will be calculated
-     * @return a non-negative value which is used to determine whether this a false touch. The
-     *         bigger the value the greater the chance that this a false touch.
+     * @return a non-negative value which is used to determine whether this a false touch; the
+     *         bigger the value the greater the chance that this a false touch
      */
     public abstract float getFalseTouchEvaluation(int type, Stroke stroke);
 }