Improved angles variance and added the angles percentage evaluation

Change-Id: I312529ea89707d27a7ef1a5ffd6d94427eaf6e8f
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
rename to packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
index c74339b..a6ebc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -32,17 +32,26 @@
  * 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.
+ * more strokes which are not smooth.
+ *
+ * This classifier also tries to split the stroke into two parts in 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. The classifier tries the tick option only if the first part is
+ * shorter than the second part.
+ *
+ * Additionally, the classifier classifies the angles as left angles (those angles which value is
+ * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles
+ * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles
+ * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are
+ * in the same direction (straight angles can be left angels or right angles)
  */
-public class AnglesVarianceClassifier extends StrokeClassifier {
+public class AnglesClassifier extends StrokeClassifier {
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
 
-    public AnglesVarianceClassifier(ClassifierData classifierData) {
+    public AnglesClassifier(ClassifierData classifierData) {
         mClassifierData = classifierData;
     }
 
@@ -66,10 +75,14 @@
 
     @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return AnglesVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+        Data data = mStrokeMap.get(stroke);
+        return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance())
+                + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
     }
 
     private static class Data {
+        private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
+
         private List<Point> mLastThreePoints = new ArrayList<>();
         private float mFirstAngleVariance;
         private float mPreviousAngle;
@@ -80,6 +93,12 @@
         private float mSecondSum;
         private float mCount;
         private float mSecondCount;
+        private float mFirstLength;
+        private float mLength;
+        private float mAnglesCount;
+        private float mLeftAngles;
+        private float mRightAngles;
+        private float mStraightAngles;
 
         public Data() {
             mFirstAngleVariance = 0.0f;
@@ -88,6 +107,8 @@
             mSumSquares = mSecondSumSquares = 0.0f;
             mSum = mSecondSum = 0.0f;
             mCount = mSecondCount = 1.0f;
+            mLength = mFirstLength = 0.0f;
+            mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
         }
 
         public void addPoint(Point point) {
@@ -95,6 +116,9 @@
             // Repetitions are being ignored so that proper angles are calculated.
             if (mLastThreePoints.isEmpty()
                     || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
+                if (!mLastThreePoints.isEmpty()) {
+                    mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
+                }
                 mLastThreePoints.add(point);
                 if (mLastThreePoints.size() == 4) {
                     mLastThreePoints.remove(0);
@@ -102,6 +126,15 @@
                     float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
                             mLastThreePoints.get(2));
 
+                    mAnglesCount++;
+                    if (angle < Math.PI - ANGLE_DEVIATION) {
+                        mLeftAngles++;
+                    } else if (angle <= Math.PI + ANGLE_DEVIATION) {
+                        mStraightAngles++;
+                    } else {
+                        mRightAngles++;
+                    }
+
                     float difference = angle - mPreviousAngle;
 
                     // If this is the biggest angle of the stroke so then we save the value of
@@ -109,6 +142,7 @@
                     // variance of the second part.
                     if (mBiggestAngle < angle) {
                         mBiggestAngle = angle;
+                        mFirstLength = mLength;
                         mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
                         mSecondSumSquares = 0.0f;
                         mSecondSum = 0.0f;
@@ -132,9 +166,19 @@
         }
 
         public float getAnglesVariance() {
-            return Math.min(getAnglesVariance(mSumSquares, mSum, mCount),
-                    mFirstAngleVariance + getAnglesVariance(mSecondSumSquares, mSecondSum,
-                            mSecondCount));
+            float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+            if (mFirstLength < mLength / 2f) {
+                anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
+                        + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
+            }
+            return anglesVariance;
+        }
+
+        public float getAnglesPercentage() {
+            if (mAnglesCount == 0.0f) {
+                return 1.0f;
+            }
+            return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java
new file mode 100644
index 0000000..a0ceb29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.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 AnglesPercentageEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 1.00) evaluation++;
+        if (value < 0.95) evaluation++;
+        if (value < 0.90) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 0e45ac1..7c8721c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -69,13 +69,13 @@
         mClassifierData = new ClassifierData(mDpi);
         mHistoryEvaluator = new HistoryEvaluator();
 
-        mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData));
+        mStrokeClassifiers.add(new AnglesClassifier(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 SpeedAnglesClassifier(mClassifierData));
         mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
 
         mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
index 1ea467b..cedf467 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -17,8 +17,11 @@
 package com.android.systemui.classifier;
 
 /**
- * A classifier which looks at the ratio between the duration of the stroke and its number of
- * points.
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points. The number of points is subtracted by 2 because the UP event comes in with some delay
+ * and it should not influence the ratio and also strokes which are long and have a small number
+ * of points are punished more (these kind of strokes are usually bad ones and they tend to score
+ * well in other classifiers).
  */
 public class LengthCountClassifier extends StrokeClassifier {
     public LengthCountClassifier(ClassifierData classifierData) {
@@ -26,6 +29,7 @@
 
     @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return LengthCountEvaluator.evaluate(stroke.getTotalLength() / stroke.getCount());
+        return LengthCountEvaluator.evaluate(stroke.getTotalLength()
+                / Math.max(1.0f, stroke.getCount() - 2));
     }
 }
\ 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
index 68f163d1..dac7a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
@@ -23,10 +23,11 @@
 public class LengthCountEvaluator {
     public static float evaluate(float value) {
         float evaluation = 0.0f;
-        if (value < 0.07) evaluation++;
+        if (value < 0.09) evaluation++;
         if (value < 0.05) evaluation++;
         if (value < 0.02) evaluation++;
         if (value > 0.6) evaluation++;
+        if (value > 0.9) evaluation++;
         if (value > 1.2) evaluation++;
         return evaluation;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
rename to packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
index 9a30fe1..d544a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -28,14 +28,17 @@
  * 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
+ * {@link AnglesClassifier} (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
+ * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
+ * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
+ * does that is because the speed of a good stroke is most often increases, so most of these angels
+ * should be in this interval.
  */
-public class SpeedVarianceClassifier extends StrokeClassifier {
+public class SpeedAnglesClassifier extends StrokeClassifier {
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
 
-    public SpeedVarianceClassifier(ClassifierData classifierData) {
+    public SpeedAnglesClassifier(ClassifierData classifierData) {
         mClassifierData = classifierData;
     }
 
@@ -64,12 +67,15 @@
 
     @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
-        return SpeedVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+        Data data = mStrokeMap.get(stroke);
+        return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
+                + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
     }
 
     private static class Data {
         private final float DURATION_SCALE = 1e8f;
         private final float LENGTH_SCALE = 1.0f;
+        private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
 
         private List<Point> mLastThreePoints = new ArrayList<>();
         private Point mPreviousPoint;
@@ -78,6 +84,8 @@
         private float mSum;
         private float mCount;
         private float mDist;
+        private float mAnglesCount;
+        private float mAcceleratingAngles;
 
         public Data() {
             mPreviousPoint = null;
@@ -86,6 +94,7 @@
             mSum = 0.0f;
             mCount = 1.0f;
             mDist = 0.0f;
+            mAnglesCount = mAcceleratingAngles = 0.0f;
         }
 
         public void addPoint(Point point) {
@@ -108,6 +117,11 @@
                     float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
                             mLastThreePoints.get(2));
 
+                    mAnglesCount++;
+                    if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
+                        mAcceleratingAngles++;
+                    }
+
                     float difference = angle - mPreviousAngle;
                     mSum += difference;
                     mSumSquares += difference * difference;
@@ -120,5 +134,12 @@
         public float getAnglesVariance() {
             return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
         }
+
+        public float getAnglesPercentage() {
+            if (mAnglesCount == 0.0f) {
+                return 1.0f;
+            }
+            return (mAcceleratingAngles) / mAnglesCount;
+        }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java
new file mode 100644
index 0000000..2a45fa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.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 SpeedAnglesPercentageEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 1.00) evaluation++;
+        if (value < 0.95) evaluation++;
+        if (value < 0.90) evaluation++;
+        return evaluation;
+    }
+}