aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/SwipeTracker.java
blob: 970e9196560160d22f9ad2ef904392010b8ea60b (about) (plain) (blame)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright (C) 2010 Google Inc.
 *
 * 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.inputmethod.latin;

import android.view.MotionEvent;

class SwipeTracker {
    private static final int NUM_PAST = 4;
    private static final int LONGEST_PAST_TIME = 200;

    final EventRingBuffer mBuffer = new EventRingBuffer(NUM_PAST);

    private float mYVelocity;
    private float mXVelocity;

    public void addMovement(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mBuffer.clear();
            return;
        }
        long time = ev.getEventTime();
        final int count = ev.getHistorySize();
        for (int i = 0; i < count; i++) {
            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), ev.getHistoricalEventTime(i));
        }
        addPoint(ev.getX(), ev.getY(), time);
    }

    private void addPoint(float x, float y, long time) {
        final EventRingBuffer buffer = mBuffer;
        while (buffer.size() > 0) {
            long lastT = buffer.getTime(0);
            if (lastT >= time - LONGEST_PAST_TIME)
                break;
            buffer.dropOldest();
        }
        buffer.add(x, y, time);
    }

    public void computeCurrentVelocity(int units) {
        computeCurrentVelocity(units, Float.MAX_VALUE);
    }

    public void computeCurrentVelocity(int units, float maxVelocity) {
        final EventRingBuffer buffer = mBuffer;
        final float oldestX = buffer.getX(0);
        final float oldestY = buffer.getY(0);
        final long oldestTime = buffer.getTime(0);

        float accumX = 0;
        float accumY = 0;
        final int count = buffer.size();
        for (int pos = 1; pos < count; pos++) {
            final int dur = (int)(buffer.getTime(pos) - oldestTime);
            if (dur == 0) continue;
            float dist = buffer.getX(pos) - oldestX;
            float vel = (dist / dur) * units;   // pixels/frame.
            if (accumX == 0) accumX = vel;
            else accumX = (accumX + vel) * .5f;

            dist = buffer.getY(pos) - oldestY;
            vel = (dist / dur) * units;   // pixels/frame.
            if (accumY == 0) accumY = vel;
            else accumY = (accumY + vel) * .5f;
        }
        mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
                : Math.min(accumX, maxVelocity);
        mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
                : Math.min(accumY, maxVelocity);
    }

    public float getXVelocity() {
        return mXVelocity;
    }

    public float getYVelocity() {
        return mYVelocity;
    }

    static class EventRingBuffer {
        private final int bufSize;
        private final float xBuf[];
        private final float yBuf[];
        private final long timeBuf[];
        private int top;  // points new event
        private int end;  // points oldest event
        private int count; // the number of valid data

        public EventRingBuffer(int max) {
            this.bufSize = max;
            xBuf = new float[max];
            yBuf = new float[max];
            timeBuf = new long[max];
            clear();
        }

        public void clear() {
            top = end = count = 0;
        }

        public int size() {
            return count;
        }

        // Position 0 points oldest event
        private int index(int pos) {
            return (end + pos) % bufSize;
        }

        private int advance(int index) {
            return (index + 1) % bufSize;
        }

        public void add(float x, float y, long time) {
            xBuf[top] = x;
            yBuf[top] = y;
            timeBuf[top] = time;
            top = advance(top);
            if (count < bufSize) {
                count++;
            } else {
                end = advance(end);
            }
        }

        public float getX(int pos) {
            return xBuf[index(pos)];
        }

        public float getY(int pos) {
            return yBuf[index(pos)];
        }

        public long getTime(int pos) {
            return timeBuf[index(pos)];
        }

        public void dropOldest() {
            count--;
            end = advance(end);
        }
    }
}