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