diff options
Diffstat (limited to 'java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java')
-rw-r--r-- | java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java new file mode 100644 index 000000000..36ae3ad7c --- /dev/null +++ b/java/com/android/incallui/answer/impl/classifier/SpeedAnglesClassifier.java @@ -0,0 +1,147 @@ +/* + * 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.incallui.answer.impl.classifier; + +import android.util.ArrayMap; +import android.view.MotionEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 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 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. 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. + */ +class SpeedAnglesClassifier extends StrokeClassifier { + private Map<Stroke, Data> mStrokeMap = new ArrayMap<>(); + + public SpeedAnglesClassifier(ClassifierData classifierData) { + mClassifierData = classifierData; + } + + @Override + public String getTag() { + return "SPD_ANG"; + } + + @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(Stroke stroke) { + Data data = mStrokeMap.get(stroke); + return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance()) + + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage()); + } + + private static class Data { + private static final float DURATION_SCALE = 1e8f; + private static final float LENGTH_SCALE = 1.0f; + private static final float ANGLE_DEVIATION = (float) Math.PI / 10.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; + private float mAnglesCount; + private float mAcceleratingAngles; + + public Data() { + mPreviousPoint = null; + mPreviousAngle = (float) Math.PI; + mSumSquares = 0.0f; + mSum = 0.0f; + mCount = 1.0f; + mDist = 0.0f; + mAnglesCount = mAcceleratingAngles = 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)); + + mAnglesCount++; + if (angle >= (float) Math.PI - ANGLE_DEVIATION) { + mAcceleratingAngles++; + } + + float difference = angle - mPreviousAngle; + mSum += difference; + mSumSquares += difference * difference; + mCount += 1.0f; + mPreviousAngle = angle; + } + } + } + + public float getAnglesVariance() { + return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount); + } + + public float getAnglesPercentage() { + if (mAnglesCount == 0.0f) { + return 1.0f; + } + return (mAcceleratingAngles) / mAnglesCount; + } + } +} |