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.incallui.answer.impl.classifier; 18 19 import android.os.SystemClock; 20 import java.util.ArrayList; 21 22 /** 23 * Holds the evaluations for ended strokes and gestures. These values are decreased through time. 24 */ 25 class HistoryEvaluator { 26 private static final float INTERVAL = 50.0f; 27 private static final float HISTORY_FACTOR = 0.9f; 28 private static final float EPSILON = 1e-5f; 29 30 private final ArrayList<Data> strokes = new ArrayList<>(); 31 private final ArrayList<Data> gestureWeights = new ArrayList<>(); 32 private long lastUpdate; 33 HistoryEvaluator()34 public HistoryEvaluator() { 35 lastUpdate = SystemClock.elapsedRealtime(); 36 } 37 addStroke(float evaluation)38 public void addStroke(float evaluation) { 39 decayValue(); 40 strokes.add(new Data(evaluation)); 41 } 42 addGesture(float evaluation)43 public void addGesture(float evaluation) { 44 decayValue(); 45 gestureWeights.add(new Data(evaluation)); 46 } 47 48 /** Calculates the weighted average of strokes and adds to it the weighted average of gestures */ getEvaluation()49 public float getEvaluation() { 50 return weightedAverage(strokes) + weightedAverage(gestureWeights); 51 } 52 weightedAverage(ArrayList<Data> list)53 private float weightedAverage(ArrayList<Data> list) { 54 float sumValue = 0.0f; 55 float sumWeight = 0.0f; 56 int size = list.size(); 57 for (int i = 0; i < size; i++) { 58 Data data = list.get(i); 59 sumValue += data.evaluation * data.weight; 60 sumWeight += data.weight; 61 } 62 63 if (sumWeight == 0.0f) { 64 return 0.0f; 65 } 66 67 return sumValue / sumWeight; 68 } 69 decayValue()70 private void decayValue() { 71 long time = SystemClock.elapsedRealtime(); 72 73 if (time <= lastUpdate) { 74 return; 75 } 76 77 // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds. 78 float factor = (float) Math.pow(HISTORY_FACTOR, (time - lastUpdate) / INTERVAL); 79 80 decayValue(strokes, factor); 81 decayValue(gestureWeights, factor); 82 lastUpdate = time; 83 } 84 decayValue(ArrayList<Data> list, float factor)85 private void decayValue(ArrayList<Data> list, float factor) { 86 int size = list.size(); 87 for (int i = 0; i < size; i++) { 88 list.get(i).weight *= factor; 89 } 90 91 // Removing evaluations with such small weights that they do not matter anymore 92 while (!list.isEmpty() && isZero(list.get(0).weight)) { 93 list.remove(0); 94 } 95 } 96 isZero(float x)97 private boolean isZero(float x) { 98 return x <= EPSILON && x >= -EPSILON; 99 } 100 101 /** 102 * For each stroke it holds its initial value and the current weight. Initially the weight is set 103 * to 1.0 104 */ 105 private static class Data { 106 public float evaluation; 107 public float weight; 108 Data(float evaluation)109 public Data(float evaluation) { 110 this.evaluation = evaluation; 111 weight = 1.0f; 112 } 113 } 114 } 115