The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package android.view; |
| 18 | |
| 19 | import android.util.Config; |
| 20 | import android.util.Log; |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 21 | import android.util.Poolable; |
| 22 | import android.util.Pool; |
Romain Guy | 2e9bbce | 2009-04-01 10:40:10 -0700 | [diff] [blame] | 23 | import android.util.Pools; |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 24 | import android.util.PoolableManager; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 25 | |
| 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 Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 35 | public final class VelocityTracker implements Poolable<VelocityTracker> { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 36 | private static final String TAG = "VelocityTracker"; |
| 37 | private static final boolean DEBUG = false; |
| 38 | private static final boolean localLOGV = DEBUG || Config.LOGV; |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 39 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 40 | private static final int NUM_PAST = 10; |
| 41 | private static final int MAX_AGE_MILLISECONDS = 200; |
| 42 | |
| 43 | private static final int POINTER_POOL_CAPACITY = 20; |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 44 | private static final int INVALID_POINTER = -1; |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 45 | |
Romain Guy | 2e9bbce | 2009-04-01 10:40:10 -0700 | [diff] [blame] | 46 | private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( |
| 47 | Pools.finitePool(new PoolableManager<VelocityTracker>() { |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 48 | public VelocityTracker newInstance() { |
| 49 | return new VelocityTracker(); |
| 50 | } |
| 51 | |
| 52 | public void onAcquired(VelocityTracker element) { |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | public void onReleased(VelocityTracker element) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 56 | element.clear(); |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 57 | } |
| 58 | }, 2)); |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 59 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 60 | private static Pointer sRecycledPointerListHead; |
| 61 | private static int sRecycledPointerCount; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 62 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 63 | private static final class Pointer { |
| 64 | public Pointer next; |
| 65 | |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 66 | public int id; |
| 67 | public float xVelocity; |
| 68 | public float yVelocity; |
| 69 | |
| 70 | public final float[] pastX = new float[NUM_PAST]; |
| 71 | public final float[] pastY = new float[NUM_PAST]; |
| 72 | public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 73 | |
| 74 | public int generation; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 75 | } |
| 76 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 77 | private Pointer mPointerListHead; // sorted by id in increasing order |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 78 | private int mLastTouchIndex; |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 79 | private int mGeneration; |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 80 | private int mActivePointerId; |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 81 | |
| 82 | private VelocityTracker mNext; |
| 83 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 84 | /** |
| 85 | * Retrieve a new VelocityTracker object to watch the velocity of a |
| 86 | * motion. Be sure to call {@link #recycle} when done. You should |
| 87 | * generally only maintain an active object while tracking a movement, |
| 88 | * so that the VelocityTracker can be re-used elsewhere. |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 89 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 90 | * @return Returns a new VelocityTracker. |
| 91 | */ |
| 92 | static public VelocityTracker obtain() { |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 93 | return sPool.acquire(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 94 | } |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 95 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 96 | /** |
| 97 | * Return a VelocityTracker object back to be re-used by others. You must |
| 98 | * not touch the object after calling this function. |
| 99 | */ |
| 100 | public void recycle() { |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 101 | sPool.release(this); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 102 | } |
Romain Guy | d928d68 | 2009-03-31 17:52:16 -0700 | [diff] [blame] | 103 | |
| 104 | /** |
| 105 | * @hide |
| 106 | */ |
| 107 | public void setNextPoolable(VelocityTracker element) { |
| 108 | mNext = element; |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * @hide |
| 113 | */ |
| 114 | public VelocityTracker getNextPoolable() { |
| 115 | return mNext; |
| 116 | } |
| 117 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 118 | private VelocityTracker() { |
Adam Powell | 0bba68d | 2010-03-03 19:50:49 -0800 | [diff] [blame] | 119 | clear(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Reset the velocity tracker back to its initial state. |
| 124 | */ |
| 125 | public void clear() { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 126 | releasePointerList(mPointerListHead); |
| 127 | |
| 128 | mPointerListHead = null; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 129 | mLastTouchIndex = 0; |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 130 | mActivePointerId = INVALID_POINTER; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Add a user's movement to the tracker. You should call this for the |
| 135 | * initial {@link MotionEvent#ACTION_DOWN}, the following |
| 136 | * {@link MotionEvent#ACTION_MOVE} events that you receive, and the |
| 137 | * final {@link MotionEvent#ACTION_UP}. You can, however, call this |
| 138 | * for whichever events you desire. |
| 139 | * |
| 140 | * @param ev The MotionEvent you received and would like to track. |
| 141 | */ |
| 142 | public void addMovement(MotionEvent ev) { |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 143 | final int historySize = ev.getHistorySize(); |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 144 | final int pointerCount = ev.getPointerCount(); |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 145 | final int lastTouchIndex = mLastTouchIndex; |
| 146 | final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; |
| 147 | final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 148 | final int generation = mGeneration++; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 149 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 150 | mLastTouchIndex = finalTouchIndex; |
| 151 | |
| 152 | // Update pointer data. |
| 153 | Pointer previousPointer = null; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 154 | for (int i = 0; i < pointerCount; i++){ |
| 155 | final int pointerId = ev.getPointerId(i); |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 156 | |
| 157 | // Find the pointer data for this pointer id. |
| 158 | // This loop is optimized for the common case where pointer ids in the event |
| 159 | // are in sorted order. However, we check for this case explicitly and |
| 160 | // perform a full linear scan from the start if needed. |
| 161 | Pointer nextPointer; |
| 162 | if (previousPointer == null || pointerId < previousPointer.id) { |
| 163 | previousPointer = null; |
| 164 | nextPointer = mPointerListHead; |
| 165 | } else { |
| 166 | nextPointer = previousPointer.next; |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 167 | } |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 168 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 169 | final Pointer pointer; |
| 170 | for (;;) { |
| 171 | if (nextPointer != null) { |
| 172 | final int nextPointerId = nextPointer.id; |
| 173 | if (nextPointerId == pointerId) { |
| 174 | pointer = nextPointer; |
| 175 | break; |
| 176 | } |
| 177 | if (nextPointerId < pointerId) { |
| 178 | nextPointer = nextPointer.next; |
| 179 | continue; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // Pointer went down. Add it to the list. |
| 184 | // Write a sentinel at the end of the pastTime trace so we will be able to |
| 185 | // tell when the trace started. |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 186 | if (mActivePointerId == INVALID_POINTER) { |
| 187 | // Congratulations! You're the new active pointer! |
| 188 | mActivePointerId = pointerId; |
| 189 | } |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 190 | pointer = obtainPointer(); |
| 191 | pointer.id = pointerId; |
| 192 | pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE; |
| 193 | pointer.next = nextPointer; |
| 194 | if (previousPointer == null) { |
| 195 | mPointerListHead = pointer; |
| 196 | } else { |
| 197 | previousPointer.next = pointer; |
| 198 | } |
| 199 | break; |
| 200 | } |
| 201 | |
| 202 | pointer.generation = generation; |
| 203 | previousPointer = pointer; |
| 204 | |
| 205 | final float[] pastX = pointer.pastX; |
| 206 | final float[] pastY = pointer.pastY; |
| 207 | final long[] pastTime = pointer.pastTime; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 208 | |
| 209 | for (int j = 0; j < historySize; j++) { |
| 210 | final int touchIndex = (nextTouchIndex + j) % NUM_PAST; |
| 211 | pastX[touchIndex] = ev.getHistoricalX(i, j); |
| 212 | pastY[touchIndex] = ev.getHistoricalY(i, j); |
| 213 | pastTime[touchIndex] = ev.getHistoricalEventTime(j); |
| 214 | } |
| 215 | pastX[finalTouchIndex] = ev.getX(i); |
| 216 | pastY[finalTouchIndex] = ev.getY(i); |
| 217 | pastTime[finalTouchIndex] = ev.getEventTime(); |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 218 | } |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 219 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 220 | // Find removed pointers. |
| 221 | previousPointer = null; |
| 222 | for (Pointer pointer = mPointerListHead; pointer != null; ) { |
| 223 | final Pointer nextPointer = pointer.next; |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 224 | final int pointerId = pointer.id; |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 225 | if (pointer.generation != generation) { |
| 226 | // Pointer went up. Remove it from the list. |
| 227 | if (previousPointer == null) { |
| 228 | mPointerListHead = nextPointer; |
| 229 | } else { |
| 230 | previousPointer.next = nextPointer; |
| 231 | } |
| 232 | releasePointer(pointer); |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 233 | |
| 234 | if (pointerId == mActivePointerId) { |
| 235 | // Pick a new active pointer. How is arbitrary. |
| 236 | mActivePointerId = mPointerListHead != null ? |
| 237 | mPointerListHead.id : INVALID_POINTER; |
| 238 | } |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 239 | } else { |
| 240 | previousPointer = pointer; |
| 241 | } |
| 242 | pointer = nextPointer; |
| 243 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 244 | } |
Romain Guy | 4296fc4 | 2009-07-06 11:48:52 -0700 | [diff] [blame] | 245 | |
| 246 | /** |
| 247 | * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum |
| 248 | * velocity of Float.MAX_VALUE. |
| 249 | * |
| 250 | * @see #computeCurrentVelocity(int, float) |
| 251 | */ |
| 252 | public void computeCurrentVelocity(int units) { |
| 253 | computeCurrentVelocity(units, Float.MAX_VALUE); |
| 254 | } |
| 255 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 256 | /** |
| 257 | * Compute the current velocity based on the points that have been |
| 258 | * collected. Only call this when you actually want to retrieve velocity |
| 259 | * information, as it is relatively expensive. You can then retrieve |
| 260 | * the velocity with {@link #getXVelocity()} and |
| 261 | * {@link #getYVelocity()}. |
| 262 | * |
| 263 | * @param units The units you would like the velocity in. A value of 1 |
| 264 | * provides pixels per millisecond, 1000 provides pixels per second, etc. |
Romain Guy | 4296fc4 | 2009-07-06 11:48:52 -0700 | [diff] [blame] | 265 | * @param maxVelocity The maximum velocity that can be computed by this method. |
| 266 | * This value must be declared in the same unit as the units parameter. This value |
| 267 | * must be positive. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 268 | */ |
Romain Guy | 4296fc4 | 2009-07-06 11:48:52 -0700 | [diff] [blame] | 269 | public void computeCurrentVelocity(int units, float maxVelocity) { |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 270 | final int lastTouchIndex = mLastTouchIndex; |
Marc Capdevielle | ce760cd | 2010-02-03 11:15:38 +0100 | [diff] [blame] | 271 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 272 | for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { |
| 273 | final long[] pastTime = pointer.pastTime; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 274 | |
| 275 | // Search backwards in time for oldest acceptable time. |
| 276 | // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE. |
| 277 | int oldestTouchIndex = lastTouchIndex; |
| 278 | int numTouches = 1; |
| 279 | final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS; |
| 280 | while (numTouches < NUM_PAST) { |
| 281 | final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST; |
| 282 | final long nextOldestTime = pastTime[nextOldestTouchIndex]; |
| 283 | if (nextOldestTime < minTime) { // also handles end of trace sentinel |
| 284 | break; |
Adam Powell | 87da860 | 2010-02-09 13:19:25 -0800 | [diff] [blame] | 285 | } |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 286 | oldestTouchIndex = nextOldestTouchIndex; |
| 287 | numTouches += 1; |
Marc Capdevielle | ce760cd | 2010-02-03 11:15:38 +0100 | [diff] [blame] | 288 | } |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 289 | |
| 290 | // If we have a lot of samples, skip the last received sample since it is |
| 291 | // probably pretty noisy compared to the sum of all of the traces already acquired. |
| 292 | if (numTouches > 3) { |
| 293 | numTouches -= 1; |
| 294 | } |
| 295 | |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 296 | // Kind-of stupid. |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 297 | final float[] pastX = pointer.pastX; |
| 298 | final float[] pastY = pointer.pastY; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 299 | |
| 300 | final float oldestX = pastX[oldestTouchIndex]; |
| 301 | final float oldestY = pastY[oldestTouchIndex]; |
| 302 | final long oldestTime = pastTime[oldestTouchIndex]; |
| 303 | |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 304 | float accumX = 0; |
| 305 | float accumY = 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 306 | |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 307 | for (int i = 1; i < numTouches; i++) { |
| 308 | final int touchIndex = (oldestTouchIndex + i) % NUM_PAST; |
| 309 | final int duration = (int)(pastTime[touchIndex] - oldestTime); |
| 310 | |
| 311 | if (duration == 0) continue; |
| 312 | |
| 313 | float delta = pastX[touchIndex] - oldestX; |
| 314 | float velocity = (delta / duration) * units; // pixels/frame. |
| 315 | accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f; |
| 316 | |
| 317 | delta = pastY[touchIndex] - oldestY; |
| 318 | velocity = (delta / duration) * units; // pixels/frame. |
| 319 | accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f; |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 320 | } |
Adam Powell | 87da860 | 2010-02-09 13:19:25 -0800 | [diff] [blame] | 321 | |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 322 | if (accumX < -maxVelocity) { |
| 323 | accumX = - maxVelocity; |
| 324 | } else if (accumX > maxVelocity) { |
| 325 | accumX = maxVelocity; |
| 326 | } |
| 327 | |
| 328 | if (accumY < -maxVelocity) { |
| 329 | accumY = - maxVelocity; |
| 330 | } else if (accumY > maxVelocity) { |
| 331 | accumY = maxVelocity; |
| 332 | } |
| 333 | |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 334 | pointer.xVelocity = accumX; |
| 335 | pointer.yVelocity = accumY; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 336 | |
| 337 | if (localLOGV) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 338 | Log.v(TAG, "Pointer " + pointer.id |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 339 | + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); |
| 340 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 341 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Retrieve the last computed X velocity. You must first call |
| 346 | * {@link #computeCurrentVelocity(int)} before calling this function. |
| 347 | * |
| 348 | * @return The previously computed X velocity. |
| 349 | */ |
| 350 | public float getXVelocity() { |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 351 | Pointer pointer = getPointer(mActivePointerId); |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 352 | return pointer != null ? pointer.xVelocity : 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Retrieve the last computed Y velocity. You must first call |
| 357 | * {@link #computeCurrentVelocity(int)} before calling this function. |
| 358 | * |
| 359 | * @return The previously computed Y velocity. |
| 360 | */ |
| 361 | public float getYVelocity() { |
Adam Powell | 8ae0f3f | 2010-09-09 11:54:19 -0700 | [diff] [blame] | 362 | Pointer pointer = getPointer(mActivePointerId); |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 363 | return pointer != null ? pointer.yVelocity : 0; |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Retrieve the last computed X velocity. You must first call |
| 368 | * {@link #computeCurrentVelocity(int)} before calling this function. |
| 369 | * |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 370 | * @param id Which pointer's velocity to return. |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 371 | * @return The previously computed X velocity. |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 372 | */ |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 373 | public float getXVelocity(int id) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 374 | Pointer pointer = getPointer(id); |
| 375 | return pointer != null ? pointer.xVelocity : 0; |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | /** |
| 379 | * Retrieve the last computed Y velocity. You must first call |
| 380 | * {@link #computeCurrentVelocity(int)} before calling this function. |
| 381 | * |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 382 | * @param id Which pointer's velocity to return. |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 383 | * @return The previously computed Y velocity. |
Adam Powell | 8acdb20 | 2010-01-06 17:33:52 -0800 | [diff] [blame] | 384 | */ |
Adam Powell | 73d8fca | 2010-02-12 16:50:19 -0800 | [diff] [blame] | 385 | public float getYVelocity(int id) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 386 | Pointer pointer = getPointer(id); |
| 387 | return pointer != null ? pointer.yVelocity : 0; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 388 | } |
| 389 | |
Romain Guy | c00972b | 2010-10-12 11:31:07 -0700 | [diff] [blame] | 390 | private Pointer getPointer(int id) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 391 | for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { |
| 392 | if (pointer.id == id) { |
| 393 | return pointer; |
Jeff Brown | 9e2ad36 | 2010-07-30 19:20:11 -0700 | [diff] [blame] | 394 | } |
| 395 | } |
| 396 | return null; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 397 | } |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 398 | |
Romain Guy | c00972b | 2010-10-12 11:31:07 -0700 | [diff] [blame] | 399 | private static Pointer obtainPointer() { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 400 | synchronized (sPool) { |
| 401 | if (sRecycledPointerCount != 0) { |
| 402 | Pointer element = sRecycledPointerListHead; |
| 403 | sRecycledPointerCount -= 1; |
| 404 | sRecycledPointerListHead = element.next; |
| 405 | element.next = null; |
| 406 | return element; |
| 407 | } |
| 408 | } |
| 409 | return new Pointer(); |
| 410 | } |
| 411 | |
Romain Guy | c00972b | 2010-10-12 11:31:07 -0700 | [diff] [blame] | 412 | private static void releasePointer(Pointer pointer) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 413 | synchronized (sPool) { |
| 414 | if (sRecycledPointerCount < POINTER_POOL_CAPACITY) { |
| 415 | pointer.next = sRecycledPointerListHead; |
| 416 | sRecycledPointerCount += 1; |
| 417 | sRecycledPointerListHead = pointer; |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | |
Romain Guy | c00972b | 2010-10-12 11:31:07 -0700 | [diff] [blame] | 422 | private static void releasePointerList(Pointer pointer) { |
Jeff Brown | 88cf2fc | 2010-08-09 18:50:35 -0700 | [diff] [blame] | 423 | if (pointer != null) { |
| 424 | synchronized (sPool) { |
| 425 | int count = sRecycledPointerCount; |
| 426 | if (count >= POINTER_POOL_CAPACITY) { |
| 427 | return; |
| 428 | } |
| 429 | |
| 430 | Pointer tail = pointer; |
| 431 | for (;;) { |
| 432 | count += 1; |
| 433 | if (count >= POINTER_POOL_CAPACITY) { |
| 434 | break; |
| 435 | } |
| 436 | |
| 437 | Pointer next = tail.next; |
| 438 | if (next == null) { |
| 439 | break; |
| 440 | } |
| 441 | tail = next; |
| 442 | } |
| 443 | |
| 444 | tail.next = sRecycledPointerListHead; |
| 445 | sRecycledPointerCount = count; |
| 446 | sRecycledPointerListHead = pointer; |
| 447 | } |
| 448 | } |
| 449 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 450 | } |