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