summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/statusbar/phone/NoisyVelocityTracker.java
blob: 214dda2b60b71d203a6810412279552cc6362be4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright (C) 2014 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.systemui.statusbar.phone;

import android.util.Log;
import android.util.Pools;
import android.view.MotionEvent;

import java.util.ArrayDeque;
import java.util.Iterator;

/**
 * A very simple low-pass velocity filter for motion events for noisy touch screens.
 */
public class NoisyVelocityTracker implements VelocityTrackerInterface {

    private static final Pools.SynchronizedPool<NoisyVelocityTracker> sNoisyPool =
            new Pools.SynchronizedPool<>(2);

    private static final float DECAY = 0.75f;
    private static final boolean DEBUG = false;

    private final int MAX_EVENTS = 8;
    private ArrayDeque<MotionEventCopy> mEventBuf = new ArrayDeque<MotionEventCopy>(MAX_EVENTS);
    private float mVX, mVY = 0;

    private static class MotionEventCopy {
        public MotionEventCopy(float x2, float y2, long eventTime) {
            this.x = x2;
            this.y = y2;
            this.t = eventTime;
        }
        float x, y;
        long t;
    }

    public static NoisyVelocityTracker obtain() {
        NoisyVelocityTracker instance = sNoisyPool.acquire();
        return (instance != null) ? instance : new NoisyVelocityTracker();
    }

    private NoisyVelocityTracker() {
    }

    public void addMovement(MotionEvent event) {
        if (mEventBuf.size() == MAX_EVENTS) {
            mEventBuf.remove();
        }
        mEventBuf.add(new MotionEventCopy(event.getX(), event.getY(), event.getEventTime()));
    }

    public void computeCurrentVelocity(int units) {
        if (NoisyVelocityTracker.DEBUG) {
            Log.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events");
        }
        mVX = mVY = 0;
        MotionEventCopy last = null;
        int i = 0;
        float totalweight = 0f;
        float weight = 10f;
        for (final Iterator<MotionEventCopy> iter = mEventBuf.iterator();
                iter.hasNext();) {
            final MotionEventCopy event = iter.next();
            if (last != null) {
                final float dt = (float) (event.t - last.t) / units;
                final float dx = (event.x - last.x);
                final float dy = (event.y - last.y);
                if (NoisyVelocityTracker.DEBUG) {
                    Log.v("FlingTracker", String.format(
                            "   [%d] (t=%d %.1f,%.1f) dx=%.1f dy=%.1f dt=%f vx=%.1f vy=%.1f",
                            i, event.t, event.x, event.y,
                            dx, dy, dt,
                            (dx/dt),
                            (dy/dt)
                    ));
                }
                if (event.t == last.t) {
                    // Really not sure what to do with events that happened at the same time,
                    // so we'll skip subsequent events.
                    continue;
                }
                mVX += weight * dx / dt;
                mVY += weight * dy / dt;
                totalweight += weight;
                weight *= DECAY;
            }
            last = event;
            i++;
        }
        if (totalweight > 0) {
            mVX /= totalweight;
            mVY /= totalweight;
        } else {
            // so as not to contaminate the velocities with NaN
            mVX = mVY = 0;
        }

        if (NoisyVelocityTracker.DEBUG) {
            Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
        }
    }

    public float getXVelocity() {
        if (Float.isNaN(mVX) || Float.isInfinite(mVX)) {
            mVX = 0;
        }
        return mVX;
    }

    public float getYVelocity() {
        if (Float.isNaN(mVY) || Float.isInfinite(mVX)) {
            mVY = 0;
        }
        return mVY;
    }

    public void recycle() {
        mEventBuf.clear();
        sNoisyPool.release(this);
    }
}