Loading core/java/android/view/VelocityTracker.java +44 −291 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.view; import android.util.Config; import android.util.Log; import android.util.Poolable; import android.util.Pool; import android.util.Pools; Loading @@ -25,24 +23,15 @@ import android.util.PoolableManager; /** * Helper for tracking the velocity of touch events, for implementing * flinging and other such gestures. Use {@link #obtain} to retrieve a * new instance of the class when you are going to begin tracking, put * the motion events you receive into it with {@link #addMovement(MotionEvent)}, * and when you want to determine the velocity call * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()} * and {@link #getXVelocity()}. * flinging and other such gestures. * * Use {@link #obtain} to retrieve a new instance of the class when you are going * to begin tracking. Put the motion events you receive into it with * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)} * and {@link #getXVelocity(int)} to retrieve the velocity for each pointer id. */ public final class VelocityTracker implements Poolable<VelocityTracker> { private static final String TAG = "VelocityTracker"; private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG || Config.LOGV; private static final int NUM_PAST = 10; private static final int MAX_AGE_MILLISECONDS = 200; private static final int POINTER_POOL_CAPACITY = 20; private static final int INVALID_POINTER = -1; private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( Pools.finitePool(new PoolableManager<VelocityTracker>() { public VelocityTracker newInstance() { Loading @@ -57,30 +46,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } }, 2)); private static Pointer sRecycledPointerListHead; private static int sRecycledPointerCount; private static final class Pointer { public Pointer next; public int id; public float xVelocity; public float yVelocity; public final float[] pastX = new float[NUM_PAST]; public final float[] pastY = new float[NUM_PAST]; public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel public int generation; } private Pointer mPointerListHead; // sorted by id in increasing order private int mLastTouchIndex; private int mGeneration; private int mActivePointerId; private static final int ACTIVE_POINTER_ID = -1; private int mPtr; private VelocityTracker mNext; private static native int nativeInitialize(); private static native void nativeDispose(int ptr); private static native void nativeClear(int ptr); private static native void nativeAddMovement(int ptr, MotionEvent event); private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity); private static native float nativeGetXVelocity(int ptr, int id); private static native float nativeGetYVelocity(int ptr, int id); /** * Retrieve a new VelocityTracker object to watch the velocity of a * motion. Be sure to call {@link #recycle} when done. You should Loading Loading @@ -116,18 +94,26 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } private VelocityTracker() { clear(); mPtr = nativeInitialize(); } @Override protected void finalize() throws Throwable { try { if (mPtr != 0) { nativeDispose(mPtr); mPtr = 0; } } finally { super.finalize(); } } /** * Reset the velocity tracker back to its initial state. */ public void clear() { releasePointerList(mPointerListHead); mPointerListHead = null; mLastTouchIndex = 0; mActivePointerId = INVALID_POINTER; nativeClear(mPtr); } /** Loading @@ -137,110 +123,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * final {@link MotionEvent#ACTION_UP}. You can, however, call this * for whichever events you desire. * * @param ev The MotionEvent you received and would like to track. * @param event The MotionEvent you received and would like to track. */ public void addMovement(MotionEvent ev) { final int historySize = ev.getHistorySize(); final int pointerCount = ev.getPointerCount(); final int lastTouchIndex = mLastTouchIndex; final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; final int generation = mGeneration++; mLastTouchIndex = finalTouchIndex; // Update pointer data. Pointer previousPointer = null; for (int i = 0; i < pointerCount; i++){ final int pointerId = ev.getPointerId(i); // Find the pointer data for this pointer id. // This loop is optimized for the common case where pointer ids in the event // are in sorted order. However, we check for this case explicitly and // perform a full linear scan from the start if needed. Pointer nextPointer; if (previousPointer == null || pointerId < previousPointer.id) { previousPointer = null; nextPointer = mPointerListHead; } else { nextPointer = previousPointer.next; } final Pointer pointer; for (;;) { if (nextPointer != null) { final int nextPointerId = nextPointer.id; if (nextPointerId == pointerId) { pointer = nextPointer; break; } if (nextPointerId < pointerId) { nextPointer = nextPointer.next; continue; } } // Pointer went down. Add it to the list. // Write a sentinel at the end of the pastTime trace so we will be able to // tell when the trace started. if (mActivePointerId == INVALID_POINTER) { // Congratulations! You're the new active pointer! mActivePointerId = pointerId; } pointer = obtainPointer(); pointer.id = pointerId; pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE; pointer.next = nextPointer; if (previousPointer == null) { mPointerListHead = pointer; } else { previousPointer.next = pointer; } break; } pointer.generation = generation; previousPointer = pointer; final float[] pastX = pointer.pastX; final float[] pastY = pointer.pastY; final long[] pastTime = pointer.pastTime; for (int j = 0; j < historySize; j++) { final int touchIndex = (nextTouchIndex + j) % NUM_PAST; pastX[touchIndex] = ev.getHistoricalX(i, j); pastY[touchIndex] = ev.getHistoricalY(i, j); pastTime[touchIndex] = ev.getHistoricalEventTime(j); } pastX[finalTouchIndex] = ev.getX(i); pastY[finalTouchIndex] = ev.getY(i); pastTime[finalTouchIndex] = ev.getEventTime(); } // Find removed pointers. previousPointer = null; for (Pointer pointer = mPointerListHead; pointer != null; ) { final Pointer nextPointer = pointer.next; final int pointerId = pointer.id; if (pointer.generation != generation) { // Pointer went up. Remove it from the list. if (previousPointer == null) { mPointerListHead = nextPointer; } else { previousPointer.next = nextPointer; } releasePointer(pointer); if (pointerId == mActivePointerId) { // Pick a new active pointer. How is arbitrary. mActivePointerId = mPointerListHead != null ? mPointerListHead.id : INVALID_POINTER; } } else { previousPointer = pointer; } pointer = nextPointer; public void addMovement(MotionEvent event) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } nativeAddMovement(mPtr, event); } /** Loading @@ -250,7 +139,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @see #computeCurrentVelocity(int, float) */ public void computeCurrentVelocity(int units) { computeCurrentVelocity(units, Float.MAX_VALUE); nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE); } /** Loading @@ -267,78 +156,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { final int lastTouchIndex = mLastTouchIndex; for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { final long[] pastTime = pointer.pastTime; // Search backwards in time for oldest acceptable time. // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE. int oldestTouchIndex = lastTouchIndex; int numTouches = 1; final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS; while (numTouches < NUM_PAST) { final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST; final long nextOldestTime = pastTime[nextOldestTouchIndex]; if (nextOldestTime < minTime) { // also handles end of trace sentinel break; } oldestTouchIndex = nextOldestTouchIndex; numTouches += 1; } // If we have a lot of samples, skip the last received sample since it is // probably pretty noisy compared to the sum of all of the traces already acquired. if (numTouches > 3) { numTouches -= 1; } // Kind-of stupid. final float[] pastX = pointer.pastX; final float[] pastY = pointer.pastY; final float oldestX = pastX[oldestTouchIndex]; final float oldestY = pastY[oldestTouchIndex]; final long oldestTime = pastTime[oldestTouchIndex]; float accumX = 0; float accumY = 0; for (int i = 1; i < numTouches; i++) { final int touchIndex = (oldestTouchIndex + i) % NUM_PAST; final int duration = (int)(pastTime[touchIndex] - oldestTime); if (duration == 0) continue; float delta = pastX[touchIndex] - oldestX; float velocity = (delta / duration) * units; // pixels/frame. accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f; delta = pastY[touchIndex] - oldestY; velocity = (delta / duration) * units; // pixels/frame. accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f; } if (accumX < -maxVelocity) { accumX = - maxVelocity; } else if (accumX > maxVelocity) { accumX = maxVelocity; } if (accumY < -maxVelocity) { accumY = - maxVelocity; } else if (accumY > maxVelocity) { accumY = maxVelocity; } pointer.xVelocity = accumX; pointer.yVelocity = accumY; if (localLOGV) { Log.v(TAG, "Pointer " + pointer.id + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); } } nativeComputeCurrentVelocity(mPtr, units, maxVelocity); } /** Loading @@ -348,8 +166,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { Pointer pointer = getPointer(mActivePointerId); return pointer != null ? pointer.xVelocity : 0; return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID); } /** Loading @@ -359,8 +176,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { Pointer pointer = getPointer(mActivePointerId); return pointer != null ? pointer.yVelocity : 0; return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID); } /** Loading @@ -371,8 +187,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity(int id) { Pointer pointer = getPointer(id); return pointer != null ? pointer.xVelocity : 0; return nativeGetXVelocity(mPtr, id); } /** Loading @@ -383,68 +198,6 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity(int id) { Pointer pointer = getPointer(id); return pointer != null ? pointer.yVelocity : 0; } private Pointer getPointer(int id) { for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { if (pointer.id == id) { return pointer; } } return null; } private static Pointer obtainPointer() { synchronized (sPool) { if (sRecycledPointerCount != 0) { Pointer element = sRecycledPointerListHead; sRecycledPointerCount -= 1; sRecycledPointerListHead = element.next; element.next = null; return element; } } return new Pointer(); } private static void releasePointer(Pointer pointer) { synchronized (sPool) { if (sRecycledPointerCount < POINTER_POOL_CAPACITY) { pointer.next = sRecycledPointerListHead; sRecycledPointerCount += 1; sRecycledPointerListHead = pointer; } } } private static void releasePointerList(Pointer pointer) { if (pointer != null) { synchronized (sPool) { int count = sRecycledPointerCount; if (count >= POINTER_POOL_CAPACITY) { return; } Pointer tail = pointer; for (;;) { count += 1; if (count >= POINTER_POOL_CAPACITY) { break; } Pointer next = tail.next; if (next == null) { break; } tail = next; } tail.next = sRecycledPointerListHead; sRecycledPointerCount = count; sRecycledPointerListHead = pointer; } } return nativeGetYVelocity(mPtr, id); } } core/jni/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ LOCAL_SRC_FILES:= \ android_view_KeyCharacterMap.cpp \ android_view_GLES20Canvas.cpp \ android_view_MotionEvent.cpp \ android_view_VelocityTracker.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ android_os_Debug.cpp \ Loading core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -170,6 +170,7 @@ extern int register_android_view_InputChannel(JNIEnv* env); extern int register_android_view_InputQueue(JNIEnv* env); extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_view_VelocityTracker(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); Loading Loading @@ -1302,6 +1303,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_InputQueue), REG_JNI(register_android_view_KeyEvent), REG_JNI(register_android_view_MotionEvent), REG_JNI(register_android_view_VelocityTracker), REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), Loading core/jni/android_view_InputQueue.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -380,7 +380,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName()); #endif inputEventObj = android_view_MotionEvent_fromNative(env, inputEventObj = android_view_MotionEvent_obtainAsCopy(env, static_cast<MotionEvent*>(inputEvent)); dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent; break; Loading core/jni/android_view_MotionEvent.cpp +7 −22 Original line number Diff line number Diff line Loading @@ -57,7 +57,10 @@ static struct { // ---------------------------------------------------------------------------- static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) { MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) { if (!eventObj) { return NULL; } return reinterpret_cast<MotionEvent*>( env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr)); } Loading @@ -68,10 +71,10 @@ static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj, reinterpret_cast<int>(event)); } jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) { jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) { jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain); if (env->ExceptionCheck()) { if (env->ExceptionCheck() || !eventObj) { LOGE("An exception occurred while obtaining a motion event."); LOGE_EX(env); env->ExceptionClear(); Loading @@ -88,18 +91,6 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even return eventObj; } status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, MotionEvent* event) { MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj); if (!srcEvent) { LOGE("MotionEvent was finalized"); return BAD_VALUE; } event->copyFrom(srcEvent, true); return OK; } status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); if (env->ExceptionCheck()) { Loading Loading @@ -500,13 +491,7 @@ static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass claz static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz, jint nativePtr, jint pointerId) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); size_t pointerCount = event->getPointerCount(); for (size_t i = 0; i < pointerCount; i++) { if (event->getPointerId(i) == pointerId) { return i; } } return -1; return jint(event->findPointerIndex(pointerId)); } static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz, Loading Loading
core/java/android/view/VelocityTracker.java +44 −291 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.view; import android.util.Config; import android.util.Log; import android.util.Poolable; import android.util.Pool; import android.util.Pools; Loading @@ -25,24 +23,15 @@ import android.util.PoolableManager; /** * Helper for tracking the velocity of touch events, for implementing * flinging and other such gestures. Use {@link #obtain} to retrieve a * new instance of the class when you are going to begin tracking, put * the motion events you receive into it with {@link #addMovement(MotionEvent)}, * and when you want to determine the velocity call * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()} * and {@link #getXVelocity()}. * flinging and other such gestures. * * Use {@link #obtain} to retrieve a new instance of the class when you are going * to begin tracking. Put the motion events you receive into it with * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)} * and {@link #getXVelocity(int)} to retrieve the velocity for each pointer id. */ public final class VelocityTracker implements Poolable<VelocityTracker> { private static final String TAG = "VelocityTracker"; private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG || Config.LOGV; private static final int NUM_PAST = 10; private static final int MAX_AGE_MILLISECONDS = 200; private static final int POINTER_POOL_CAPACITY = 20; private static final int INVALID_POINTER = -1; private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( Pools.finitePool(new PoolableManager<VelocityTracker>() { public VelocityTracker newInstance() { Loading @@ -57,30 +46,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } }, 2)); private static Pointer sRecycledPointerListHead; private static int sRecycledPointerCount; private static final class Pointer { public Pointer next; public int id; public float xVelocity; public float yVelocity; public final float[] pastX = new float[NUM_PAST]; public final float[] pastY = new float[NUM_PAST]; public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel public int generation; } private Pointer mPointerListHead; // sorted by id in increasing order private int mLastTouchIndex; private int mGeneration; private int mActivePointerId; private static final int ACTIVE_POINTER_ID = -1; private int mPtr; private VelocityTracker mNext; private static native int nativeInitialize(); private static native void nativeDispose(int ptr); private static native void nativeClear(int ptr); private static native void nativeAddMovement(int ptr, MotionEvent event); private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity); private static native float nativeGetXVelocity(int ptr, int id); private static native float nativeGetYVelocity(int ptr, int id); /** * Retrieve a new VelocityTracker object to watch the velocity of a * motion. Be sure to call {@link #recycle} when done. You should Loading Loading @@ -116,18 +94,26 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } private VelocityTracker() { clear(); mPtr = nativeInitialize(); } @Override protected void finalize() throws Throwable { try { if (mPtr != 0) { nativeDispose(mPtr); mPtr = 0; } } finally { super.finalize(); } } /** * Reset the velocity tracker back to its initial state. */ public void clear() { releasePointerList(mPointerListHead); mPointerListHead = null; mLastTouchIndex = 0; mActivePointerId = INVALID_POINTER; nativeClear(mPtr); } /** Loading @@ -137,110 +123,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * final {@link MotionEvent#ACTION_UP}. You can, however, call this * for whichever events you desire. * * @param ev The MotionEvent you received and would like to track. * @param event The MotionEvent you received and would like to track. */ public void addMovement(MotionEvent ev) { final int historySize = ev.getHistorySize(); final int pointerCount = ev.getPointerCount(); final int lastTouchIndex = mLastTouchIndex; final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; final int generation = mGeneration++; mLastTouchIndex = finalTouchIndex; // Update pointer data. Pointer previousPointer = null; for (int i = 0; i < pointerCount; i++){ final int pointerId = ev.getPointerId(i); // Find the pointer data for this pointer id. // This loop is optimized for the common case where pointer ids in the event // are in sorted order. However, we check for this case explicitly and // perform a full linear scan from the start if needed. Pointer nextPointer; if (previousPointer == null || pointerId < previousPointer.id) { previousPointer = null; nextPointer = mPointerListHead; } else { nextPointer = previousPointer.next; } final Pointer pointer; for (;;) { if (nextPointer != null) { final int nextPointerId = nextPointer.id; if (nextPointerId == pointerId) { pointer = nextPointer; break; } if (nextPointerId < pointerId) { nextPointer = nextPointer.next; continue; } } // Pointer went down. Add it to the list. // Write a sentinel at the end of the pastTime trace so we will be able to // tell when the trace started. if (mActivePointerId == INVALID_POINTER) { // Congratulations! You're the new active pointer! mActivePointerId = pointerId; } pointer = obtainPointer(); pointer.id = pointerId; pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE; pointer.next = nextPointer; if (previousPointer == null) { mPointerListHead = pointer; } else { previousPointer.next = pointer; } break; } pointer.generation = generation; previousPointer = pointer; final float[] pastX = pointer.pastX; final float[] pastY = pointer.pastY; final long[] pastTime = pointer.pastTime; for (int j = 0; j < historySize; j++) { final int touchIndex = (nextTouchIndex + j) % NUM_PAST; pastX[touchIndex] = ev.getHistoricalX(i, j); pastY[touchIndex] = ev.getHistoricalY(i, j); pastTime[touchIndex] = ev.getHistoricalEventTime(j); } pastX[finalTouchIndex] = ev.getX(i); pastY[finalTouchIndex] = ev.getY(i); pastTime[finalTouchIndex] = ev.getEventTime(); } // Find removed pointers. previousPointer = null; for (Pointer pointer = mPointerListHead; pointer != null; ) { final Pointer nextPointer = pointer.next; final int pointerId = pointer.id; if (pointer.generation != generation) { // Pointer went up. Remove it from the list. if (previousPointer == null) { mPointerListHead = nextPointer; } else { previousPointer.next = nextPointer; } releasePointer(pointer); if (pointerId == mActivePointerId) { // Pick a new active pointer. How is arbitrary. mActivePointerId = mPointerListHead != null ? mPointerListHead.id : INVALID_POINTER; } } else { previousPointer = pointer; } pointer = nextPointer; public void addMovement(MotionEvent event) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } nativeAddMovement(mPtr, event); } /** Loading @@ -250,7 +139,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @see #computeCurrentVelocity(int, float) */ public void computeCurrentVelocity(int units) { computeCurrentVelocity(units, Float.MAX_VALUE); nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE); } /** Loading @@ -267,78 +156,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { final int lastTouchIndex = mLastTouchIndex; for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { final long[] pastTime = pointer.pastTime; // Search backwards in time for oldest acceptable time. // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE. int oldestTouchIndex = lastTouchIndex; int numTouches = 1; final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS; while (numTouches < NUM_PAST) { final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST; final long nextOldestTime = pastTime[nextOldestTouchIndex]; if (nextOldestTime < minTime) { // also handles end of trace sentinel break; } oldestTouchIndex = nextOldestTouchIndex; numTouches += 1; } // If we have a lot of samples, skip the last received sample since it is // probably pretty noisy compared to the sum of all of the traces already acquired. if (numTouches > 3) { numTouches -= 1; } // Kind-of stupid. final float[] pastX = pointer.pastX; final float[] pastY = pointer.pastY; final float oldestX = pastX[oldestTouchIndex]; final float oldestY = pastY[oldestTouchIndex]; final long oldestTime = pastTime[oldestTouchIndex]; float accumX = 0; float accumY = 0; for (int i = 1; i < numTouches; i++) { final int touchIndex = (oldestTouchIndex + i) % NUM_PAST; final int duration = (int)(pastTime[touchIndex] - oldestTime); if (duration == 0) continue; float delta = pastX[touchIndex] - oldestX; float velocity = (delta / duration) * units; // pixels/frame. accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f; delta = pastY[touchIndex] - oldestY; velocity = (delta / duration) * units; // pixels/frame. accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f; } if (accumX < -maxVelocity) { accumX = - maxVelocity; } else if (accumX > maxVelocity) { accumX = maxVelocity; } if (accumY < -maxVelocity) { accumY = - maxVelocity; } else if (accumY > maxVelocity) { accumY = maxVelocity; } pointer.xVelocity = accumX; pointer.yVelocity = accumY; if (localLOGV) { Log.v(TAG, "Pointer " + pointer.id + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); } } nativeComputeCurrentVelocity(mPtr, units, maxVelocity); } /** Loading @@ -348,8 +166,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { Pointer pointer = getPointer(mActivePointerId); return pointer != null ? pointer.xVelocity : 0; return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID); } /** Loading @@ -359,8 +176,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { Pointer pointer = getPointer(mActivePointerId); return pointer != null ? pointer.yVelocity : 0; return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID); } /** Loading @@ -371,8 +187,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity(int id) { Pointer pointer = getPointer(id); return pointer != null ? pointer.xVelocity : 0; return nativeGetXVelocity(mPtr, id); } /** Loading @@ -383,68 +198,6 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity(int id) { Pointer pointer = getPointer(id); return pointer != null ? pointer.yVelocity : 0; } private Pointer getPointer(int id) { for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { if (pointer.id == id) { return pointer; } } return null; } private static Pointer obtainPointer() { synchronized (sPool) { if (sRecycledPointerCount != 0) { Pointer element = sRecycledPointerListHead; sRecycledPointerCount -= 1; sRecycledPointerListHead = element.next; element.next = null; return element; } } return new Pointer(); } private static void releasePointer(Pointer pointer) { synchronized (sPool) { if (sRecycledPointerCount < POINTER_POOL_CAPACITY) { pointer.next = sRecycledPointerListHead; sRecycledPointerCount += 1; sRecycledPointerListHead = pointer; } } } private static void releasePointerList(Pointer pointer) { if (pointer != null) { synchronized (sPool) { int count = sRecycledPointerCount; if (count >= POINTER_POOL_CAPACITY) { return; } Pointer tail = pointer; for (;;) { count += 1; if (count >= POINTER_POOL_CAPACITY) { break; } Pointer next = tail.next; if (next == null) { break; } tail = next; } tail.next = sRecycledPointerListHead; sRecycledPointerCount = count; sRecycledPointerListHead = pointer; } } return nativeGetYVelocity(mPtr, id); } }
core/jni/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ LOCAL_SRC_FILES:= \ android_view_KeyCharacterMap.cpp \ android_view_GLES20Canvas.cpp \ android_view_MotionEvent.cpp \ android_view_VelocityTracker.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ android_os_Debug.cpp \ Loading
core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -170,6 +170,7 @@ extern int register_android_view_InputChannel(JNIEnv* env); extern int register_android_view_InputQueue(JNIEnv* env); extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_view_VelocityTracker(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); Loading Loading @@ -1302,6 +1303,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_InputQueue), REG_JNI(register_android_view_KeyEvent), REG_JNI(register_android_view_MotionEvent), REG_JNI(register_android_view_VelocityTracker), REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), Loading
core/jni/android_view_InputQueue.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -380,7 +380,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName()); #endif inputEventObj = android_view_MotionEvent_fromNative(env, inputEventObj = android_view_MotionEvent_obtainAsCopy(env, static_cast<MotionEvent*>(inputEvent)); dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent; break; Loading
core/jni/android_view_MotionEvent.cpp +7 −22 Original line number Diff line number Diff line Loading @@ -57,7 +57,10 @@ static struct { // ---------------------------------------------------------------------------- static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) { MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) { if (!eventObj) { return NULL; } return reinterpret_cast<MotionEvent*>( env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr)); } Loading @@ -68,10 +71,10 @@ static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj, reinterpret_cast<int>(event)); } jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) { jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) { jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain); if (env->ExceptionCheck()) { if (env->ExceptionCheck() || !eventObj) { LOGE("An exception occurred while obtaining a motion event."); LOGE_EX(env); env->ExceptionClear(); Loading @@ -88,18 +91,6 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even return eventObj; } status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, MotionEvent* event) { MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj); if (!srcEvent) { LOGE("MotionEvent was finalized"); return BAD_VALUE; } event->copyFrom(srcEvent, true); return OK; } status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); if (env->ExceptionCheck()) { Loading Loading @@ -500,13 +491,7 @@ static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass claz static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz, jint nativePtr, jint pointerId) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); size_t pointerCount = event->getPointerCount(); for (size_t i = 0; i < pointerCount; i++) { if (event->getPointerId(i) == pointerId) { return i; } } return -1; return jint(event->findPointerIndex(pointerId)); } static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz, Loading