Loading core/java/android/view/InputQueue.java +42 −6 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ public final class InputQueue { final InputChannel mChannel; private static Object sLock = new Object(); private static final Object sLock = new Object(); private static native void nativeRegisterInputChannel(InputChannel inputChannel, InputHandler inputHandler, MessageQueue messageQueue); Loading Loading @@ -103,28 +103,64 @@ public final class InputQueue { @SuppressWarnings("unused") private static void dispatchKeyEvent(InputHandler inputHandler, KeyEvent event, long finishedToken) { Runnable finishedCallback = new FinishedCallback(finishedToken); Runnable finishedCallback = FinishedCallback.obtain(finishedToken); inputHandler.handleKey(event, finishedCallback); } @SuppressWarnings("unused") private static void dispatchMotionEvent(InputHandler inputHandler, MotionEvent event, long finishedToken) { Runnable finishedCallback = new FinishedCallback(finishedToken); Runnable finishedCallback = FinishedCallback.obtain(finishedToken); inputHandler.handleMotion(event, finishedCallback); } // TODO consider recycling finished callbacks when done private static class FinishedCallback implements Runnable { private static final boolean DEBUG_RECYCLING = false; private static final int RECYCLE_MAX_COUNT = 4; private static FinishedCallback sRecycleHead; private static int sRecycleCount; private FinishedCallback mRecycleNext; private long mFinishedToken; public FinishedCallback(long finishedToken) { mFinishedToken = finishedToken; private FinishedCallback() { } public static FinishedCallback obtain(long finishedToken) { synchronized (sLock) { FinishedCallback callback = sRecycleHead; if (callback != null) { callback.mRecycleNext = null; sRecycleHead = callback.mRecycleNext; sRecycleCount -= 1; } else { callback = new FinishedCallback(); } callback.mFinishedToken = finishedToken; return callback; } } public void run() { synchronized (sLock) { if (mFinishedToken == -1) { throw new IllegalStateException("Event finished callback already invoked."); } nativeFinished(mFinishedToken); mFinishedToken = -1; if (sRecycleCount < RECYCLE_MAX_COUNT) { mRecycleNext = sRecycleHead; sRecycleHead = this; sRecycleCount += 1; if (DEBUG_RECYCLING) { Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount); } } } } } Loading core/java/android/view/MotionEvent.java +17 −24 Original line number Diff line number Diff line Loading @@ -177,72 +177,65 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int EDGE_RIGHT = 0x00000008; /** /* * Offset for the sample's X coordinate. * @hide */ static private final int SAMPLE_X = 0; /** /* * Offset for the sample's Y coordinate. * @hide */ static private final int SAMPLE_Y = 1; /** /* * Offset for the sample's pressure. * @hide */ static private final int SAMPLE_PRESSURE = 2; /** /* * Offset for the sample's size * @hide */ static private final int SAMPLE_SIZE = 3; /** /* * Offset for the sample's touch major axis length. * @hide */ static private final int SAMPLE_TOUCH_MAJOR = 4; /** /* * Offset for the sample's touch minor axis length. * @hide */ static private final int SAMPLE_TOUCH_MINOR = 5; /** /* * Offset for the sample's tool major axis length. * @hide */ static private final int SAMPLE_TOOL_MAJOR = 6; /** /* * Offset for the sample's tool minor axis length. * @hide */ static private final int SAMPLE_TOOL_MINOR = 7; /** /* * Offset for the sample's orientation. * @hide */ static private final int SAMPLE_ORIENTATION = 8; /** /* * Number of data items for each sample. * @hide */ static private final int NUM_SAMPLE_DATA = 9; /** * Number of possible pointers. * @hide /* * Minimum number of pointers for which to reserve space when allocating new * motion events. This is explicitly not a bound on the maximum number of pointers. */ static public final int BASE_AVAIL_POINTERS = 5; static private final int BASE_AVAIL_POINTERS = 5; /* * Minimum number of samples for which to reserve space when allocating new motion events. */ static private final int BASE_AVAIL_SAMPLES = 8; static private final int MAX_RECYCLED = 10; Loading core/java/android/view/VelocityTracker.java +168 −85 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { static final boolean localLOGV = DEBUG || Config.LOGV; static final int NUM_PAST = 10; static final int LONGEST_PAST_TIME = 200; static final int MAX_AGE_MILLISECONDS = 200; static final VelocityTracker[] mPool = new VelocityTracker[1]; private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( Loading @@ -55,13 +55,21 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } }, 2)); final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; private static final int INITIAL_POINTERS = 5; float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; int mLastTouch; private static final class PointerData { 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 } private PointerData[] mPointers = new PointerData[INITIAL_POINTERS]; private int mNumPointers; private int mLastTouchIndex; private VelocityTracker mNext; Loading Loading @@ -107,12 +115,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Reset the velocity tracker back to its initial state. */ public void clear() { final long[][] pastTime = mPastTime; for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) { for (int i = 0; i < NUM_PAST; i++) { pastTime[p][i] = Long.MIN_VALUE; } } mNumPointers = 0; mLastTouchIndex = 0; } /** Loading @@ -125,37 +129,74 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @param ev The MotionEvent you received and would like to track. */ public void addMovement(MotionEvent ev) { final int N = ev.getHistorySize(); final int historySize = ev.getHistorySize(); final int pointerCount = ev.getPointerCount(); int touchIndex = (mLastTouch + 1) % NUM_PAST; for (int i=0; i<N; i++) { for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { mPastTime[id][touchIndex] = Long.MIN_VALUE; final int lastTouchIndex = mLastTouchIndex; final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; if (pointerCount < mNumPointers) { final PointerData[] pointers = mPointers; int i = mNumPointers; while (--i >= 0) { final PointerData pointerData = pointers[i]; if (ev.findPointerIndex(pointerData.id) == -1) { // Pointer went up. // Shuffle pointers down to fill the hole. Place the old pointer data at // the end so we can recycle it if more pointers are added later. mNumPointers -= 1; final int remaining = mNumPointers - i; if (remaining != 0) { System.arraycopy(pointers, i + 1, pointers, i, remaining); pointers[mNumPointers] = pointerData; } } } for (int p = 0; p < pointerCount; p++) { int id = ev.getPointerId(p); mPastX[id][touchIndex] = ev.getHistoricalX(p, i); mPastY[id][touchIndex] = ev.getHistoricalY(p, i); mPastTime[id][touchIndex] = ev.getHistoricalEventTime(i); } touchIndex = (touchIndex + 1) % NUM_PAST; for (int i = 0; i < pointerCount; i++){ final int pointerId = ev.getPointerId(i); PointerData pointerData = getPointerData(pointerId); if (pointerData == null) { // Pointer went down. // Add a new entry. Write a sentinel at the end of the pastTime trace so we // will be able to tell where the trace started. final PointerData[] oldPointers = mPointers; final int newPointerIndex = mNumPointers; if (newPointerIndex < oldPointers.length) { pointerData = oldPointers[newPointerIndex]; if (pointerData == null) { pointerData = new PointerData(); oldPointers[newPointerIndex] = pointerData; } } else { final PointerData[] newPointers = new PointerData[newPointerIndex * 2]; System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex); mPointers = newPointers; pointerData = new PointerData(); newPointers[newPointerIndex] = pointerData; } pointerData.id = pointerId; pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE; mNumPointers += 1; } final float[] pastX = pointerData.pastX; final float[] pastY = pointerData.pastY; final long[] pastTime = pointerData.pastTime; // During calculation any pointer values with a time of MIN_VALUE are treated // as a break in input. Initialize all to MIN_VALUE for each new touch index. for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { mPastTime[id][touchIndex] = Long.MIN_VALUE; 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); } final long time = ev.getEventTime(); for (int p = 0; p < pointerCount; p++) { int id = ev.getPointerId(p); mPastX[id][touchIndex] = ev.getX(p); mPastY[id][touchIndex] = ev.getY(p); mPastTime[id][touchIndex] = time; pastX[finalTouchIndex] = ev.getX(i); pastY[finalTouchIndex] = ev.getY(i); pastTime[finalTouchIndex] = ev.getEventTime(); } mLastTouch = touchIndex; mLastTouchIndex = finalTouchIndex; } /** Loading @@ -182,54 +223,80 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) { final float[] pastX = mPastX[pos]; final float[] pastY = mPastY[pos]; final long[] pastTime = mPastTime[pos]; final int lastTouch = mLastTouch; // find oldest acceptable time int oldestTouch = lastTouch; if (pastTime[lastTouch] != Long.MIN_VALUE) { // cleared ? final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME; int nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; while (pastTime[nextOldestTouch] >= acceptableTime && nextOldestTouch != lastTouch) { oldestTouch = nextOldestTouch; nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; final int numPointers = mNumPointers; final PointerData[] pointers = mPointers; final int lastTouchIndex = mLastTouchIndex; for (int p = 0; p < numPointers; p++) { final PointerData pointerData = pointers[p]; final long[] pastTime = pointerData.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 oldestX = pastX[oldestTouch]; final float oldestY = pastY[oldestTouch]; final long oldestTime = pastTime[oldestTouch]; final float[] pastX = pointerData.pastX; final float[] pastY = pointerData.pastY; final float oldestX = pastX[oldestTouchIndex]; final float oldestY = pastY[oldestTouchIndex]; final long oldestTime = pastTime[oldestTouchIndex]; float accumX = 0; float accumY = 0; int N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1; // Skip the last received event, since it is probably pretty noisy. if (N > 3) N--; for (int i=1; i < N; i++) { final int j = (oldestTouch + i) % NUM_PAST; final int dur = (int)(pastTime[j] - oldestTime); if (dur == 0) continue; float dist = pastX[j] - oldestX; float vel = (dist/dur) * units; // pixels/frame. accumX = (accumX == 0) ? vel : (accumX + vel) * .5f; 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; dist = pastY[j] - oldestY; vel = (dist/dur) * units; // pixels/frame. accumY = (accumY == 0) ? vel : (accumY + vel) * .5f; delta = pastY[touchIndex] - oldestY; velocity = (delta / duration) * units; // pixels/frame. accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f; } mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); if (accumX < -maxVelocity) { accumX = - maxVelocity; } else if (accumX > maxVelocity) { accumX = maxVelocity; } if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" + mXVelocity + " N=" + N); if (accumY < -maxVelocity) { accumY = - maxVelocity; } else if (accumY > maxVelocity) { accumY = maxVelocity; } pointerData.xVelocity = accumX; pointerData.yVelocity = accumY; if (localLOGV) { Log.v(TAG, "[" + p + "] Pointer " + pointerData.id + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); } } } Loading @@ -240,7 +307,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { return mXVelocity[0]; PointerData pointerData = getPointerData(0); return pointerData != null ? pointerData.xVelocity : 0; } /** Loading @@ -250,7 +318,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { return mYVelocity[0]; PointerData pointerData = getPointerData(0); return pointerData != null ? pointerData.yVelocity : 0; } /** Loading @@ -261,7 +330,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity(int id) { return mXVelocity[id]; PointerData pointerData = getPointerData(id); return pointerData != null ? pointerData.xVelocity : 0; } /** Loading @@ -272,6 +342,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity(int id) { return mYVelocity[id]; PointerData pointerData = getPointerData(id); return pointerData != null ? pointerData.yVelocity : 0; } private final PointerData getPointerData(int id) { final PointerData[] pointers = mPointers; final int numPointers = mNumPointers; for (int p = 0; p < numPointers; p++) { PointerData pointerData = pointers[p]; if (pointerData.id == id) { return pointerData; } } return null; } } core/java/com/android/internal/widget/PointerLocationView.java +19 −17 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ public class PointerLocationView extends View { private float mCurPressure; private float mCurSize; private int mCurWidth; private VelocityTracker mVelocity; private float mXVelocity; private float mYVelocity; } private final ViewConfiguration mVC; Loading @@ -56,6 +57,8 @@ public class PointerLocationView extends View { private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>(); private final VelocityTracker mVelocity; private boolean mPrintCoords = true; public PointerLocationView(Context c) { Loading Loading @@ -88,8 +91,9 @@ public class PointerLocationView extends View { mPaint.setStrokeWidth(1); PointerState ps = new PointerState(); ps.mVelocity = VelocityTracker.obtain(); mPointers.add(ps); mVelocity = VelocityTracker.obtain(); } public void setPrintCoords(boolean state) { Loading Loading @@ -146,11 +150,11 @@ public class PointerLocationView extends View { } canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint); int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000); int velocity = (int) (ps.mXVelocity * 1000); canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint); canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint); velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000); velocity = (int) (ps.mYVelocity * 1000); canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint); canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint); Loading Loading @@ -205,14 +209,10 @@ public class PointerLocationView extends View { } if (drawn) { if (ps.mVelocity != null) { mPaint.setARGB(255, 255, 64, 128); float xVel = ps.mVelocity.getXVelocity() * (1000/60); float yVel = ps.mVelocity.getYVelocity() * (1000/60); float xVel = ps.mXVelocity * (1000/60); float yVel = ps.mYVelocity * (1000/60); canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); } else { canvas.drawPoint(lastX, lastY, mPaint); } } } } Loading @@ -236,11 +236,12 @@ public class PointerLocationView extends View { // mRect.setEmpty(); //} if (action == MotionEvent.ACTION_DOWN) { mVelocity.clear(); for (int p=0; p<NP; p++) { final PointerState ps = mPointers.get(p); ps.mXs.clear(); ps.mYs.clear(); ps.mVelocity = VelocityTracker.obtain(); ps.mCurDown = false; } mPointers.get(0).mCurDown = true; Loading @@ -256,12 +257,10 @@ public class PointerLocationView extends View { final int id = event.getPointerId(index); while (NP <= id) { PointerState ps = new PointerState(); ps.mVelocity = VelocityTracker.obtain(); mPointers.add(ps); NP++; } final PointerState ps = mPointers.get(id); ps.mVelocity = VelocityTracker.obtain(); ps.mCurDown = true; if (mPrintCoords) { Log.i("Pointer", "Pointer " + (id+1) + ": DOWN"); Loading @@ -277,11 +276,12 @@ public class PointerLocationView extends View { mMaxNumPointers = mCurNumPointers; } mVelocity.addMovement(event); mVelocity.computeCurrentVelocity(1); for (int i=0; i<NI; i++) { final int id = event.getPointerId(i); final PointerState ps = mPointers.get(id); ps.mVelocity.addMovement(event); ps.mVelocity.computeCurrentVelocity(1); final int N = event.getHistorySize(); for (int j=0; j<N; j++) { if (mPrintCoords) { Loading Loading @@ -309,6 +309,8 @@ public class PointerLocationView extends View { ps.mCurPressure = event.getPressure(i); ps.mCurSize = event.getSize(i); ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3)); ps.mXVelocity = mVelocity.getXVelocity(id); ps.mYVelocity = mVelocity.getYVelocity(id); } if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { Loading include/ui/InputReader.h +3 −1 Original line number Diff line number Diff line Loading @@ -565,7 +565,9 @@ protected: for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; idToIndex[i] = other.idToIndex[i]; int id = pointers[i].id; idToIndex[id] = other.idToIndex[id]; } } Loading Loading
core/java/android/view/InputQueue.java +42 −6 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ public final class InputQueue { final InputChannel mChannel; private static Object sLock = new Object(); private static final Object sLock = new Object(); private static native void nativeRegisterInputChannel(InputChannel inputChannel, InputHandler inputHandler, MessageQueue messageQueue); Loading Loading @@ -103,28 +103,64 @@ public final class InputQueue { @SuppressWarnings("unused") private static void dispatchKeyEvent(InputHandler inputHandler, KeyEvent event, long finishedToken) { Runnable finishedCallback = new FinishedCallback(finishedToken); Runnable finishedCallback = FinishedCallback.obtain(finishedToken); inputHandler.handleKey(event, finishedCallback); } @SuppressWarnings("unused") private static void dispatchMotionEvent(InputHandler inputHandler, MotionEvent event, long finishedToken) { Runnable finishedCallback = new FinishedCallback(finishedToken); Runnable finishedCallback = FinishedCallback.obtain(finishedToken); inputHandler.handleMotion(event, finishedCallback); } // TODO consider recycling finished callbacks when done private static class FinishedCallback implements Runnable { private static final boolean DEBUG_RECYCLING = false; private static final int RECYCLE_MAX_COUNT = 4; private static FinishedCallback sRecycleHead; private static int sRecycleCount; private FinishedCallback mRecycleNext; private long mFinishedToken; public FinishedCallback(long finishedToken) { mFinishedToken = finishedToken; private FinishedCallback() { } public static FinishedCallback obtain(long finishedToken) { synchronized (sLock) { FinishedCallback callback = sRecycleHead; if (callback != null) { callback.mRecycleNext = null; sRecycleHead = callback.mRecycleNext; sRecycleCount -= 1; } else { callback = new FinishedCallback(); } callback.mFinishedToken = finishedToken; return callback; } } public void run() { synchronized (sLock) { if (mFinishedToken == -1) { throw new IllegalStateException("Event finished callback already invoked."); } nativeFinished(mFinishedToken); mFinishedToken = -1; if (sRecycleCount < RECYCLE_MAX_COUNT) { mRecycleNext = sRecycleHead; sRecycleHead = this; sRecycleCount += 1; if (DEBUG_RECYCLING) { Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount); } } } } } Loading
core/java/android/view/MotionEvent.java +17 −24 Original line number Diff line number Diff line Loading @@ -177,72 +177,65 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int EDGE_RIGHT = 0x00000008; /** /* * Offset for the sample's X coordinate. * @hide */ static private final int SAMPLE_X = 0; /** /* * Offset for the sample's Y coordinate. * @hide */ static private final int SAMPLE_Y = 1; /** /* * Offset for the sample's pressure. * @hide */ static private final int SAMPLE_PRESSURE = 2; /** /* * Offset for the sample's size * @hide */ static private final int SAMPLE_SIZE = 3; /** /* * Offset for the sample's touch major axis length. * @hide */ static private final int SAMPLE_TOUCH_MAJOR = 4; /** /* * Offset for the sample's touch minor axis length. * @hide */ static private final int SAMPLE_TOUCH_MINOR = 5; /** /* * Offset for the sample's tool major axis length. * @hide */ static private final int SAMPLE_TOOL_MAJOR = 6; /** /* * Offset for the sample's tool minor axis length. * @hide */ static private final int SAMPLE_TOOL_MINOR = 7; /** /* * Offset for the sample's orientation. * @hide */ static private final int SAMPLE_ORIENTATION = 8; /** /* * Number of data items for each sample. * @hide */ static private final int NUM_SAMPLE_DATA = 9; /** * Number of possible pointers. * @hide /* * Minimum number of pointers for which to reserve space when allocating new * motion events. This is explicitly not a bound on the maximum number of pointers. */ static public final int BASE_AVAIL_POINTERS = 5; static private final int BASE_AVAIL_POINTERS = 5; /* * Minimum number of samples for which to reserve space when allocating new motion events. */ static private final int BASE_AVAIL_SAMPLES = 8; static private final int MAX_RECYCLED = 10; Loading
core/java/android/view/VelocityTracker.java +168 −85 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { static final boolean localLOGV = DEBUG || Config.LOGV; static final int NUM_PAST = 10; static final int LONGEST_PAST_TIME = 200; static final int MAX_AGE_MILLISECONDS = 200; static final VelocityTracker[] mPool = new VelocityTracker[1]; private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( Loading @@ -55,13 +55,21 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } }, 2)); final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; private static final int INITIAL_POINTERS = 5; float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; int mLastTouch; private static final class PointerData { 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 } private PointerData[] mPointers = new PointerData[INITIAL_POINTERS]; private int mNumPointers; private int mLastTouchIndex; private VelocityTracker mNext; Loading Loading @@ -107,12 +115,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Reset the velocity tracker back to its initial state. */ public void clear() { final long[][] pastTime = mPastTime; for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) { for (int i = 0; i < NUM_PAST; i++) { pastTime[p][i] = Long.MIN_VALUE; } } mNumPointers = 0; mLastTouchIndex = 0; } /** Loading @@ -125,37 +129,74 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @param ev The MotionEvent you received and would like to track. */ public void addMovement(MotionEvent ev) { final int N = ev.getHistorySize(); final int historySize = ev.getHistorySize(); final int pointerCount = ev.getPointerCount(); int touchIndex = (mLastTouch + 1) % NUM_PAST; for (int i=0; i<N; i++) { for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { mPastTime[id][touchIndex] = Long.MIN_VALUE; final int lastTouchIndex = mLastTouchIndex; final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; if (pointerCount < mNumPointers) { final PointerData[] pointers = mPointers; int i = mNumPointers; while (--i >= 0) { final PointerData pointerData = pointers[i]; if (ev.findPointerIndex(pointerData.id) == -1) { // Pointer went up. // Shuffle pointers down to fill the hole. Place the old pointer data at // the end so we can recycle it if more pointers are added later. mNumPointers -= 1; final int remaining = mNumPointers - i; if (remaining != 0) { System.arraycopy(pointers, i + 1, pointers, i, remaining); pointers[mNumPointers] = pointerData; } } } for (int p = 0; p < pointerCount; p++) { int id = ev.getPointerId(p); mPastX[id][touchIndex] = ev.getHistoricalX(p, i); mPastY[id][touchIndex] = ev.getHistoricalY(p, i); mPastTime[id][touchIndex] = ev.getHistoricalEventTime(i); } touchIndex = (touchIndex + 1) % NUM_PAST; for (int i = 0; i < pointerCount; i++){ final int pointerId = ev.getPointerId(i); PointerData pointerData = getPointerData(pointerId); if (pointerData == null) { // Pointer went down. // Add a new entry. Write a sentinel at the end of the pastTime trace so we // will be able to tell where the trace started. final PointerData[] oldPointers = mPointers; final int newPointerIndex = mNumPointers; if (newPointerIndex < oldPointers.length) { pointerData = oldPointers[newPointerIndex]; if (pointerData == null) { pointerData = new PointerData(); oldPointers[newPointerIndex] = pointerData; } } else { final PointerData[] newPointers = new PointerData[newPointerIndex * 2]; System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex); mPointers = newPointers; pointerData = new PointerData(); newPointers[newPointerIndex] = pointerData; } pointerData.id = pointerId; pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE; mNumPointers += 1; } final float[] pastX = pointerData.pastX; final float[] pastY = pointerData.pastY; final long[] pastTime = pointerData.pastTime; // During calculation any pointer values with a time of MIN_VALUE are treated // as a break in input. Initialize all to MIN_VALUE for each new touch index. for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { mPastTime[id][touchIndex] = Long.MIN_VALUE; 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); } final long time = ev.getEventTime(); for (int p = 0; p < pointerCount; p++) { int id = ev.getPointerId(p); mPastX[id][touchIndex] = ev.getX(p); mPastY[id][touchIndex] = ev.getY(p); mPastTime[id][touchIndex] = time; pastX[finalTouchIndex] = ev.getX(i); pastY[finalTouchIndex] = ev.getY(i); pastTime[finalTouchIndex] = ev.getEventTime(); } mLastTouch = touchIndex; mLastTouchIndex = finalTouchIndex; } /** Loading @@ -182,54 +223,80 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) { final float[] pastX = mPastX[pos]; final float[] pastY = mPastY[pos]; final long[] pastTime = mPastTime[pos]; final int lastTouch = mLastTouch; // find oldest acceptable time int oldestTouch = lastTouch; if (pastTime[lastTouch] != Long.MIN_VALUE) { // cleared ? final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME; int nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; while (pastTime[nextOldestTouch] >= acceptableTime && nextOldestTouch != lastTouch) { oldestTouch = nextOldestTouch; nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; final int numPointers = mNumPointers; final PointerData[] pointers = mPointers; final int lastTouchIndex = mLastTouchIndex; for (int p = 0; p < numPointers; p++) { final PointerData pointerData = pointers[p]; final long[] pastTime = pointerData.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 oldestX = pastX[oldestTouch]; final float oldestY = pastY[oldestTouch]; final long oldestTime = pastTime[oldestTouch]; final float[] pastX = pointerData.pastX; final float[] pastY = pointerData.pastY; final float oldestX = pastX[oldestTouchIndex]; final float oldestY = pastY[oldestTouchIndex]; final long oldestTime = pastTime[oldestTouchIndex]; float accumX = 0; float accumY = 0; int N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1; // Skip the last received event, since it is probably pretty noisy. if (N > 3) N--; for (int i=1; i < N; i++) { final int j = (oldestTouch + i) % NUM_PAST; final int dur = (int)(pastTime[j] - oldestTime); if (dur == 0) continue; float dist = pastX[j] - oldestX; float vel = (dist/dur) * units; // pixels/frame. accumX = (accumX == 0) ? vel : (accumX + vel) * .5f; 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; dist = pastY[j] - oldestY; vel = (dist/dur) * units; // pixels/frame. accumY = (accumY == 0) ? vel : (accumY + vel) * .5f; delta = pastY[touchIndex] - oldestY; velocity = (delta / duration) * units; // pixels/frame. accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f; } mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); if (accumX < -maxVelocity) { accumX = - maxVelocity; } else if (accumX > maxVelocity) { accumX = maxVelocity; } if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" + mXVelocity + " N=" + N); if (accumY < -maxVelocity) { accumY = - maxVelocity; } else if (accumY > maxVelocity) { accumY = maxVelocity; } pointerData.xVelocity = accumX; pointerData.yVelocity = accumY; if (localLOGV) { Log.v(TAG, "[" + p + "] Pointer " + pointerData.id + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); } } } Loading @@ -240,7 +307,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { return mXVelocity[0]; PointerData pointerData = getPointerData(0); return pointerData != null ? pointerData.xVelocity : 0; } /** Loading @@ -250,7 +318,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { return mYVelocity[0]; PointerData pointerData = getPointerData(0); return pointerData != null ? pointerData.yVelocity : 0; } /** Loading @@ -261,7 +330,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity(int id) { return mXVelocity[id]; PointerData pointerData = getPointerData(id); return pointerData != null ? pointerData.xVelocity : 0; } /** Loading @@ -272,6 +342,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity(int id) { return mYVelocity[id]; PointerData pointerData = getPointerData(id); return pointerData != null ? pointerData.yVelocity : 0; } private final PointerData getPointerData(int id) { final PointerData[] pointers = mPointers; final int numPointers = mNumPointers; for (int p = 0; p < numPointers; p++) { PointerData pointerData = pointers[p]; if (pointerData.id == id) { return pointerData; } } return null; } }
core/java/com/android/internal/widget/PointerLocationView.java +19 −17 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ public class PointerLocationView extends View { private float mCurPressure; private float mCurSize; private int mCurWidth; private VelocityTracker mVelocity; private float mXVelocity; private float mYVelocity; } private final ViewConfiguration mVC; Loading @@ -56,6 +57,8 @@ public class PointerLocationView extends View { private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>(); private final VelocityTracker mVelocity; private boolean mPrintCoords = true; public PointerLocationView(Context c) { Loading Loading @@ -88,8 +91,9 @@ public class PointerLocationView extends View { mPaint.setStrokeWidth(1); PointerState ps = new PointerState(); ps.mVelocity = VelocityTracker.obtain(); mPointers.add(ps); mVelocity = VelocityTracker.obtain(); } public void setPrintCoords(boolean state) { Loading Loading @@ -146,11 +150,11 @@ public class PointerLocationView extends View { } canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint); int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000); int velocity = (int) (ps.mXVelocity * 1000); canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint); canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint); velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000); velocity = (int) (ps.mYVelocity * 1000); canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint); canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint); Loading Loading @@ -205,14 +209,10 @@ public class PointerLocationView extends View { } if (drawn) { if (ps.mVelocity != null) { mPaint.setARGB(255, 255, 64, 128); float xVel = ps.mVelocity.getXVelocity() * (1000/60); float yVel = ps.mVelocity.getYVelocity() * (1000/60); float xVel = ps.mXVelocity * (1000/60); float yVel = ps.mYVelocity * (1000/60); canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); } else { canvas.drawPoint(lastX, lastY, mPaint); } } } } Loading @@ -236,11 +236,12 @@ public class PointerLocationView extends View { // mRect.setEmpty(); //} if (action == MotionEvent.ACTION_DOWN) { mVelocity.clear(); for (int p=0; p<NP; p++) { final PointerState ps = mPointers.get(p); ps.mXs.clear(); ps.mYs.clear(); ps.mVelocity = VelocityTracker.obtain(); ps.mCurDown = false; } mPointers.get(0).mCurDown = true; Loading @@ -256,12 +257,10 @@ public class PointerLocationView extends View { final int id = event.getPointerId(index); while (NP <= id) { PointerState ps = new PointerState(); ps.mVelocity = VelocityTracker.obtain(); mPointers.add(ps); NP++; } final PointerState ps = mPointers.get(id); ps.mVelocity = VelocityTracker.obtain(); ps.mCurDown = true; if (mPrintCoords) { Log.i("Pointer", "Pointer " + (id+1) + ": DOWN"); Loading @@ -277,11 +276,12 @@ public class PointerLocationView extends View { mMaxNumPointers = mCurNumPointers; } mVelocity.addMovement(event); mVelocity.computeCurrentVelocity(1); for (int i=0; i<NI; i++) { final int id = event.getPointerId(i); final PointerState ps = mPointers.get(id); ps.mVelocity.addMovement(event); ps.mVelocity.computeCurrentVelocity(1); final int N = event.getHistorySize(); for (int j=0; j<N; j++) { if (mPrintCoords) { Loading Loading @@ -309,6 +309,8 @@ public class PointerLocationView extends View { ps.mCurPressure = event.getPressure(i); ps.mCurSize = event.getSize(i); ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3)); ps.mXVelocity = mVelocity.getXVelocity(id); ps.mYVelocity = mVelocity.getYVelocity(id); } if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { Loading
include/ui/InputReader.h +3 −1 Original line number Diff line number Diff line Loading @@ -565,7 +565,9 @@ protected: for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; idToIndex[i] = other.idToIndex[i]; int id = pointers[i].id; idToIndex[id] = other.idToIndex[id]; } } Loading