blob: c708f547642a5c527562b9e4f1de5d2283ff6b18 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.view;
18
19import android.util.Config;
20import android.util.Log;
Romain Guyd1b3dd02009-03-31 17:53:57 -070021import android.util.Poolable;
22import android.util.Pool;
Romain Guy10b40432009-04-02 09:03:39 -070023import android.util.Pools;
Romain Guyd1b3dd02009-03-31 17:53:57 -070024import android.util.PoolableManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
26/**
27 * Helper for tracking the velocity of touch events, for implementing
28 * flinging and other such gestures. Use {@link #obtain} to retrieve a
29 * new instance of the class when you are going to begin tracking, put
30 * the motion events you receive into it with {@link #addMovement(MotionEvent)},
31 * and when you want to determine the velocity call
32 * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
33 * and {@link #getXVelocity()}.
34 */
Romain Guyd1b3dd02009-03-31 17:53:57 -070035public final class VelocityTracker implements Poolable<VelocityTracker> {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036 static final String TAG = "VelocityTracker";
37 static final boolean DEBUG = false;
38 static final boolean localLOGV = DEBUG || Config.LOGV;
Romain Guyd1b3dd02009-03-31 17:53:57 -070039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 static final int NUM_PAST = 10;
41 static final int LONGEST_PAST_TIME = 200;
Romain Guyd1b3dd02009-03-31 17:53:57 -070042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 static final VelocityTracker[] mPool = new VelocityTracker[1];
Romain Guy10b40432009-04-02 09:03:39 -070044 private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
45 Pools.finitePool(new PoolableManager<VelocityTracker>() {
Romain Guyd1b3dd02009-03-31 17:53:57 -070046 public VelocityTracker newInstance() {
47 return new VelocityTracker();
48 }
49
50 public void onAcquired(VelocityTracker element) {
51 element.clear();
52 }
53
54 public void onReleased(VelocityTracker element) {
55 }
56 }, 2));
57
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 final float mPastX[] = new float[NUM_PAST];
59 final float mPastY[] = new float[NUM_PAST];
60 final long mPastTime[] = new long[NUM_PAST];
Romain Guyd1b3dd02009-03-31 17:53:57 -070061
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 float mYVelocity;
63 float mXVelocity;
Romain Guyd1b3dd02009-03-31 17:53:57 -070064
65 private VelocityTracker mNext;
66
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 /**
68 * Retrieve a new VelocityTracker object to watch the velocity of a
69 * motion. Be sure to call {@link #recycle} when done. You should
70 * generally only maintain an active object while tracking a movement,
71 * so that the VelocityTracker can be re-used elsewhere.
Romain Guyd1b3dd02009-03-31 17:53:57 -070072 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 * @return Returns a new VelocityTracker.
74 */
75 static public VelocityTracker obtain() {
Romain Guyd1b3dd02009-03-31 17:53:57 -070076 return sPool.acquire();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 }
Romain Guyd1b3dd02009-03-31 17:53:57 -070078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 /**
80 * Return a VelocityTracker object back to be re-used by others. You must
81 * not touch the object after calling this function.
82 */
83 public void recycle() {
Romain Guyd1b3dd02009-03-31 17:53:57 -070084 sPool.release(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 }
Romain Guyd1b3dd02009-03-31 17:53:57 -070086
87 /**
88 * @hide
89 */
90 public void setNextPoolable(VelocityTracker element) {
91 mNext = element;
92 }
93
94 /**
95 * @hide
96 */
97 public VelocityTracker getNextPoolable() {
98 return mNext;
99 }
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private VelocityTracker() {
102 }
103
104 /**
105 * Reset the velocity tracker back to its initial state.
106 */
107 public void clear() {
108 mPastTime[0] = 0;
109 }
110
111 /**
112 * Add a user's movement to the tracker. You should call this for the
113 * initial {@link MotionEvent#ACTION_DOWN}, the following
114 * {@link MotionEvent#ACTION_MOVE} events that you receive, and the
115 * final {@link MotionEvent#ACTION_UP}. You can, however, call this
116 * for whichever events you desire.
117 *
118 * @param ev The MotionEvent you received and would like to track.
119 */
120 public void addMovement(MotionEvent ev) {
121 long time = ev.getEventTime();
122 final int N = ev.getHistorySize();
123 for (int i=0; i<N; i++) {
124 addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
125 ev.getHistoricalEventTime(i));
126 }
127 addPoint(ev.getX(), ev.getY(), time);
128 }
129
130 private void addPoint(float x, float y, long time) {
131 int drop = -1;
132 int i;
133 if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
134 final long[] pastTime = mPastTime;
135 for (i=0; i<NUM_PAST; i++) {
136 if (pastTime[i] == 0) {
137 break;
138 } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
139 if (localLOGV) Log.v(TAG, "Dropping past too old at "
140 + i + " time=" + pastTime[i]);
141 drop = i;
142 }
143 }
144 if (localLOGV) Log.v(TAG, "Add index: " + i);
145 if (i == NUM_PAST && drop < 0) {
146 drop = 0;
147 }
148 if (drop == i) drop--;
149 final float[] pastX = mPastX;
150 final float[] pastY = mPastY;
151 if (drop >= 0) {
152 if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
153 final int start = drop+1;
154 final int count = NUM_PAST-drop-1;
155 System.arraycopy(pastX, start, pastX, 0, count);
156 System.arraycopy(pastY, start, pastY, 0, count);
157 System.arraycopy(pastTime, start, pastTime, 0, count);
158 i -= (drop+1);
159 }
160 pastX[i] = x;
161 pastY[i] = y;
162 pastTime[i] = time;
163 i++;
164 if (i < NUM_PAST) {
165 pastTime[i] = 0;
166 }
167 }
168
169 /**
170 * Compute the current velocity based on the points that have been
171 * collected. Only call this when you actually want to retrieve velocity
172 * information, as it is relatively expensive. You can then retrieve
173 * the velocity with {@link #getXVelocity()} and
174 * {@link #getYVelocity()}.
175 *
176 * @param units The units you would like the velocity in. A value of 1
177 * provides pixels per millisecond, 1000 provides pixels per second, etc.
178 */
179 public void computeCurrentVelocity(int units) {
180 final float[] pastX = mPastX;
181 final float[] pastY = mPastY;
182 final long[] pastTime = mPastTime;
183
184 // Kind-of stupid.
185 final float oldestX = pastX[0];
186 final float oldestY = pastY[0];
187 final long oldestTime = pastTime[0];
188 float accumX = 0;
189 float accumY = 0;
190 int N=0;
191 while (N < NUM_PAST) {
192 if (pastTime[N] == 0) {
193 break;
194 }
195 N++;
196 }
197 // Skip the last received event, since it is probably pretty noisy.
198 if (N > 3) N--;
199
200 for (int i=1; i < N; i++) {
201 final int dur = (int)(pastTime[i] - oldestTime);
202 if (dur == 0) continue;
203 float dist = pastX[i] - oldestX;
204 float vel = (dist/dur) * units; // pixels/frame.
205 if (accumX == 0) accumX = vel;
206 else accumX = (accumX + vel) * .5f;
207
208 dist = pastY[i] - oldestY;
209 vel = (dist/dur) * units; // pixels/frame.
210 if (accumY == 0) accumY = vel;
211 else accumY = (accumY + vel) * .5f;
212 }
213 mXVelocity = accumX;
214 mYVelocity = accumY;
215
216 if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
217 + mXVelocity + " N=" + N);
218 }
219
220 /**
221 * Retrieve the last computed X velocity. You must first call
222 * {@link #computeCurrentVelocity(int)} before calling this function.
223 *
224 * @return The previously computed X velocity.
225 */
226 public float getXVelocity() {
227 return mXVelocity;
228 }
229
230 /**
231 * Retrieve the last computed Y velocity. You must first call
232 * {@link #computeCurrentVelocity(int)} before calling this function.
233 *
234 * @return The previously computed Y velocity.
235 */
236 public float getYVelocity() {
237 return mYVelocity;
238 }
239}