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