19f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski/*
29f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * Copyright (C) 2015 The Android Open Source Project
39f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski *
49f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * Licensed under the Apache License, Version 2.0 (the "License");
59f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * you may not use this file except in compliance with the License.
69f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * You may obtain a copy of the License at
79f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski *
89f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski *      http://www.apache.org/licenses/LICENSE-2.0
99f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski *
109f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * Unless required by applicable law or agreed to in writing, software
119f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * distributed under the License is distributed on an "AS IS" BASIS,
129f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * See the License for the specific language governing permissions and
149f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * limitations under the License
159f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski */
169f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
179f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowskipackage com.android.systemui.classifier;
189f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
19bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roosimport android.os.SystemClock;
20bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos
219f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowskiimport java.util.ArrayList;
229f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
239f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski/**
249f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski * Holds the evaluations for ended strokes and gestures. These values are decreased through time.
259f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski */
269f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowskipublic class HistoryEvaluator {
279f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private static final float INTERVAL = 50.0f;
289f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private static final float HISTORY_FACTOR = 0.9f;
299f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private static final float EPSILON = 1e-5f;
309f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
319f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private final ArrayList<Data> mStrokes = new ArrayList<>();
329f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private final ArrayList<Data> mGestureWeights = new ArrayList<>();
339f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private long mLastUpdate;
349f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
359f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    public HistoryEvaluator() {
36bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        mLastUpdate = SystemClock.elapsedRealtime();
379f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
389f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
399f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    public void addStroke(float evaluation) {
409f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        decayValue();
419f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mStrokes.add(new Data(evaluation));
429f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
439f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
449f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    public void addGesture(float evaluation) {
459f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        decayValue();
469f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mGestureWeights.add(new Data(evaluation));
479f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
489f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
499f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    /**
509f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski     * Calculates the weighted average of strokes and adds to it the weighted average of gestures
519f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski     */
529f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    public float getEvaluation() {
539f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
549f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
559f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
569f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private float weightedAverage(ArrayList<Data> list) {
579f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        float sumValue = 0.0f;
589f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        float sumWeight = 0.0f;
599f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        int size = list.size();
609f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        for (int i = 0; i < size; i++) {
619f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            Data data = list.get(i);
629f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            sumValue += data.evaluation * data.weight;
639f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            sumWeight += data.weight;
649f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
659f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
669f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        if (sumWeight == 0.0f) {
679f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            return 0.0f;
689f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
699f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
709f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        return sumValue / sumWeight;
719f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
729f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
739f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private void decayValue() {
74bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        long time = SystemClock.elapsedRealtime();
75bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos
76bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        if (time <= mLastUpdate) {
77bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos            return;
78bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        }
799f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
809f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
81bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL);
829f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
839f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        decayValue(mStrokes, factor);
849f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        decayValue(mGestureWeights, factor);
85bbb6919945180f76d5776858b1a49f910c8341d8Adrian Roos        mLastUpdate = time;
869f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
879f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
889f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private void decayValue(ArrayList<Data> list, float factor) {
899f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        int size = list.size();
909f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        for (int i = 0; i < size; i++) {
919f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            list.get(i).weight *= factor;
929f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
939f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
949f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        // Removing evaluations with such small weights that they do not matter anymore
959f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        while (!list.isEmpty() && isZero(list.get(0).weight)) {
969f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            list.remove(0);
979f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
989f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
999f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
1009f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    private boolean isZero(float x) {
1019f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        return x <= EPSILON && x >= -EPSILON;
1029f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
1039f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
1049f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    /**
1059f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski     * For each stroke it holds its initial value and the current weight. Initially the
1069f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski     * weight is set to 1.0
1079f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski     */
10868d0c9b1e9f1df04b3a0e1ebb1e7ed4c18994cf3Blazej Magnowski    private static class Data {
1099f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        public float evaluation;
1109f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        public float weight;
1119f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski
1129f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        public Data(float evaluation) {
1139f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            this.evaluation = evaluation;
1149f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            weight = 1.0f;
1159f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
1169f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    }
1179f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski}
118