Merge "Enhanced VelocityTracker for > 5 pointers and fixed bugs." into gingerbread
This commit is contained in:
@@ -35,7 +35,7 @@ public final class InputQueue {
|
|||||||
|
|
||||||
final InputChannel mChannel;
|
final InputChannel mChannel;
|
||||||
|
|
||||||
private static Object sLock = new Object();
|
private static final Object sLock = new Object();
|
||||||
|
|
||||||
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
|
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
|
||||||
InputHandler inputHandler, MessageQueue messageQueue);
|
InputHandler inputHandler, MessageQueue messageQueue);
|
||||||
@@ -103,28 +103,64 @@ public final class InputQueue {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void dispatchKeyEvent(InputHandler inputHandler,
|
private static void dispatchKeyEvent(InputHandler inputHandler,
|
||||||
KeyEvent event, long finishedToken) {
|
KeyEvent event, long finishedToken) {
|
||||||
Runnable finishedCallback = new FinishedCallback(finishedToken);
|
Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
|
||||||
inputHandler.handleKey(event, finishedCallback);
|
inputHandler.handleKey(event, finishedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void dispatchMotionEvent(InputHandler inputHandler,
|
private static void dispatchMotionEvent(InputHandler inputHandler,
|
||||||
MotionEvent event, long finishedToken) {
|
MotionEvent event, long finishedToken) {
|
||||||
Runnable finishedCallback = new FinishedCallback(finishedToken);
|
Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
|
||||||
inputHandler.handleMotion(event, finishedCallback);
|
inputHandler.handleMotion(event, finishedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO consider recycling finished callbacks when done
|
|
||||||
private static class FinishedCallback implements Runnable {
|
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;
|
private long mFinishedToken;
|
||||||
|
|
||||||
public FinishedCallback(long finishedToken) {
|
private FinishedCallback() {
|
||||||
mFinishedToken = finishedToken;
|
}
|
||||||
|
|
||||||
|
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() {
|
public void run() {
|
||||||
synchronized (sLock) {
|
synchronized (sLock) {
|
||||||
|
if (mFinishedToken == -1) {
|
||||||
|
throw new IllegalStateException("Event finished callback already invoked.");
|
||||||
|
}
|
||||||
|
|
||||||
nativeFinished(mFinishedToken);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,72 +177,65 @@ public final class MotionEvent extends InputEvent implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public static final int EDGE_RIGHT = 0x00000008;
|
public static final int EDGE_RIGHT = 0x00000008;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's X coordinate.
|
* Offset for the sample's X coordinate.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_X = 0;
|
static private final int SAMPLE_X = 0;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's Y coordinate.
|
* Offset for the sample's Y coordinate.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_Y = 1;
|
static private final int SAMPLE_Y = 1;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's pressure.
|
* Offset for the sample's pressure.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_PRESSURE = 2;
|
static private final int SAMPLE_PRESSURE = 2;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's size
|
* Offset for the sample's size
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_SIZE = 3;
|
static private final int SAMPLE_SIZE = 3;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's touch major axis length.
|
* Offset for the sample's touch major axis length.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_TOUCH_MAJOR = 4;
|
static private final int SAMPLE_TOUCH_MAJOR = 4;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's touch minor axis length.
|
* Offset for the sample's touch minor axis length.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_TOUCH_MINOR = 5;
|
static private final int SAMPLE_TOUCH_MINOR = 5;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's tool major axis length.
|
* Offset for the sample's tool major axis length.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_TOOL_MAJOR = 6;
|
static private final int SAMPLE_TOOL_MAJOR = 6;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's tool minor axis length.
|
* Offset for the sample's tool minor axis length.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_TOOL_MINOR = 7;
|
static private final int SAMPLE_TOOL_MINOR = 7;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Offset for the sample's orientation.
|
* Offset for the sample's orientation.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int SAMPLE_ORIENTATION = 8;
|
static private final int SAMPLE_ORIENTATION = 8;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Number of data items for each sample.
|
* Number of data items for each sample.
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
static private final int NUM_SAMPLE_DATA = 9;
|
static private final int NUM_SAMPLE_DATA = 9;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Number of possible pointers.
|
* Minimum number of pointers for which to reserve space when allocating new
|
||||||
* @hide
|
* 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 BASE_AVAIL_SAMPLES = 8;
|
||||||
|
|
||||||
static private final int MAX_RECYCLED = 10;
|
static private final int MAX_RECYCLED = 10;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
static final boolean localLOGV = DEBUG || Config.LOGV;
|
static final boolean localLOGV = DEBUG || Config.LOGV;
|
||||||
|
|
||||||
static final int NUM_PAST = 10;
|
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];
|
static final VelocityTracker[] mPool = new VelocityTracker[1];
|
||||||
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
|
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
|
||||||
@@ -54,14 +54,22 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
public void onReleased(VelocityTracker element) {
|
public void onReleased(VelocityTracker element) {
|
||||||
}
|
}
|
||||||
}, 2));
|
}, 2));
|
||||||
|
|
||||||
final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
|
private static final int INITIAL_POINTERS = 5;
|
||||||
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 class PointerData {
|
||||||
|
public int id;
|
||||||
float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
|
public float xVelocity;
|
||||||
float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
|
public float yVelocity;
|
||||||
int mLastTouch;
|
|
||||||
|
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;
|
private VelocityTracker mNext;
|
||||||
|
|
||||||
@@ -107,12 +115,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* Reset the velocity tracker back to its initial state.
|
* Reset the velocity tracker back to its initial state.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
final long[][] pastTime = mPastTime;
|
mNumPointers = 0;
|
||||||
for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) {
|
mLastTouchIndex = 0;
|
||||||
for (int i = 0; i < NUM_PAST; i++) {
|
|
||||||
pastTime[p][i] = Long.MIN_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,37 +129,74 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* @param ev The MotionEvent you received and would like to track.
|
* @param ev The MotionEvent you received and would like to track.
|
||||||
*/
|
*/
|
||||||
public void addMovement(MotionEvent ev) {
|
public void addMovement(MotionEvent ev) {
|
||||||
final int N = ev.getHistorySize();
|
final int historySize = ev.getHistorySize();
|
||||||
final int pointerCount = ev.getPointerCount();
|
final int pointerCount = ev.getPointerCount();
|
||||||
int touchIndex = (mLastTouch + 1) % NUM_PAST;
|
final int lastTouchIndex = mLastTouchIndex;
|
||||||
for (int i=0; i<N; i++) {
|
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
|
||||||
for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) {
|
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
|
||||||
mPastTime[id][touchIndex] = Long.MIN_VALUE;
|
|
||||||
|
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);
|
for (int i = 0; i < pointerCount; i++){
|
||||||
mPastY[id][touchIndex] = ev.getHistoricalY(p, i);
|
final int pointerId = ev.getPointerId(i);
|
||||||
mPastTime[id][touchIndex] = ev.getHistoricalEventTime(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
touchIndex = (touchIndex + 1) % NUM_PAST;
|
final float[] pastX = pointerData.pastX;
|
||||||
|
final float[] pastY = pointerData.pastY;
|
||||||
|
final long[] pastTime = pointerData.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
// During calculation any pointer values with a time of MIN_VALUE are treated
|
mLastTouchIndex = finalTouchIndex;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
mLastTouch = touchIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,54 +223,80 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* must be positive.
|
* must be positive.
|
||||||
*/
|
*/
|
||||||
public void computeCurrentVelocity(int units, float maxVelocity) {
|
public void computeCurrentVelocity(int units, float maxVelocity) {
|
||||||
for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) {
|
final int numPointers = mNumPointers;
|
||||||
final float[] pastX = mPastX[pos];
|
final PointerData[] pointers = mPointers;
|
||||||
final float[] pastY = mPastY[pos];
|
final int lastTouchIndex = mLastTouchIndex;
|
||||||
final long[] pastTime = mPastTime[pos];
|
|
||||||
final int lastTouch = mLastTouch;
|
|
||||||
|
|
||||||
// find oldest acceptable time
|
for (int p = 0; p < numPointers; p++) {
|
||||||
int oldestTouch = lastTouch;
|
final PointerData pointerData = pointers[p];
|
||||||
if (pastTime[lastTouch] != Long.MIN_VALUE) { // cleared ?
|
final long[] pastTime = pointerData.pastTime;
|
||||||
final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME;
|
|
||||||
int nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST;
|
// Search backwards in time for oldest acceptable time.
|
||||||
while (pastTime[nextOldestTouch] >= acceptableTime &&
|
// Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
|
||||||
nextOldestTouch != lastTouch) {
|
int oldestTouchIndex = lastTouchIndex;
|
||||||
oldestTouch = nextOldestTouch;
|
int numTouches = 1;
|
||||||
nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST;
|
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.
|
// Kind-of stupid.
|
||||||
final float oldestX = pastX[oldestTouch];
|
final float[] pastX = pointerData.pastX;
|
||||||
final float oldestY = pastY[oldestTouch];
|
final float[] pastY = pointerData.pastY;
|
||||||
final long oldestTime = pastTime[oldestTouch];
|
|
||||||
|
final float oldestX = pastX[oldestTouchIndex];
|
||||||
|
final float oldestY = pastY[oldestTouchIndex];
|
||||||
|
final long oldestTime = pastTime[oldestTouchIndex];
|
||||||
|
|
||||||
float accumX = 0;
|
float accumX = 0;
|
||||||
float accumY = 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;
|
|
||||||
|
|
||||||
dist = pastY[j] - oldestY;
|
for (int i = 1; i < numTouches; i++) {
|
||||||
vel = (dist/dur) * units; // pixels/frame.
|
final int touchIndex = (oldestTouchIndex + i) % NUM_PAST;
|
||||||
accumY = (accumY == 0) ? vel : (accumY + vel) * .5f;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
|
if (accumX < -maxVelocity) {
|
||||||
: Math.min(accumX, maxVelocity);
|
accumX = - maxVelocity;
|
||||||
mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
|
} else if (accumX > maxVelocity) {
|
||||||
: Math.min(accumY, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +307,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* @return The previously computed X velocity.
|
* @return The previously computed X velocity.
|
||||||
*/
|
*/
|
||||||
public float getXVelocity() {
|
public float getXVelocity() {
|
||||||
return mXVelocity[0];
|
PointerData pointerData = getPointerData(0);
|
||||||
|
return pointerData != null ? pointerData.xVelocity : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,7 +318,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* @return The previously computed Y velocity.
|
* @return The previously computed Y velocity.
|
||||||
*/
|
*/
|
||||||
public float getYVelocity() {
|
public float getYVelocity() {
|
||||||
return mYVelocity[0];
|
PointerData pointerData = getPointerData(0);
|
||||||
|
return pointerData != null ? pointerData.yVelocity : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,7 +330,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* @return The previously computed X velocity.
|
* @return The previously computed X velocity.
|
||||||
*/
|
*/
|
||||||
public float getXVelocity(int id) {
|
public float getXVelocity(int id) {
|
||||||
return mXVelocity[id];
|
PointerData pointerData = getPointerData(id);
|
||||||
|
return pointerData != null ? pointerData.xVelocity : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,6 +342,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
|||||||
* @return The previously computed Y velocity.
|
* @return The previously computed Y velocity.
|
||||||
*/
|
*/
|
||||||
public float getYVelocity(int id) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public class PointerLocationView extends View {
|
|||||||
private float mCurPressure;
|
private float mCurPressure;
|
||||||
private float mCurSize;
|
private float mCurSize;
|
||||||
private int mCurWidth;
|
private int mCurWidth;
|
||||||
private VelocityTracker mVelocity;
|
private float mXVelocity;
|
||||||
|
private float mYVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ViewConfiguration mVC;
|
private final ViewConfiguration mVC;
|
||||||
@@ -56,6 +57,8 @@ public class PointerLocationView extends View {
|
|||||||
private final ArrayList<PointerState> mPointers
|
private final ArrayList<PointerState> mPointers
|
||||||
= new ArrayList<PointerState>();
|
= new ArrayList<PointerState>();
|
||||||
|
|
||||||
|
private final VelocityTracker mVelocity;
|
||||||
|
|
||||||
private boolean mPrintCoords = true;
|
private boolean mPrintCoords = true;
|
||||||
|
|
||||||
public PointerLocationView(Context c) {
|
public PointerLocationView(Context c) {
|
||||||
@@ -88,8 +91,9 @@ public class PointerLocationView extends View {
|
|||||||
mPaint.setStrokeWidth(1);
|
mPaint.setStrokeWidth(1);
|
||||||
|
|
||||||
PointerState ps = new PointerState();
|
PointerState ps = new PointerState();
|
||||||
ps.mVelocity = VelocityTracker.obtain();
|
|
||||||
mPointers.add(ps);
|
mPointers.add(ps);
|
||||||
|
|
||||||
|
mVelocity = VelocityTracker.obtain();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrintCoords(boolean state) {
|
public void setPrintCoords(boolean state) {
|
||||||
@@ -146,11 +150,11 @@ public class PointerLocationView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
|
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.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint);
|
||||||
|
|
||||||
canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
|
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.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint);
|
||||||
|
|
||||||
canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
|
canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
|
||||||
@@ -205,14 +209,10 @@ public class PointerLocationView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (drawn) {
|
if (drawn) {
|
||||||
if (ps.mVelocity != null) {
|
mPaint.setARGB(255, 255, 64, 128);
|
||||||
mPaint.setARGB(255, 255, 64, 128);
|
float xVel = ps.mXVelocity * (1000/60);
|
||||||
float xVel = ps.mVelocity.getXVelocity() * (1000/60);
|
float yVel = ps.mYVelocity * (1000/60);
|
||||||
float yVel = ps.mVelocity.getYVelocity() * (1000/60);
|
canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
|
||||||
canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
|
|
||||||
} else {
|
|
||||||
canvas.drawPoint(lastX, lastY, mPaint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,11 +236,12 @@ public class PointerLocationView extends View {
|
|||||||
// mRect.setEmpty();
|
// mRect.setEmpty();
|
||||||
//}
|
//}
|
||||||
if (action == MotionEvent.ACTION_DOWN) {
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
mVelocity.clear();
|
||||||
|
|
||||||
for (int p=0; p<NP; p++) {
|
for (int p=0; p<NP; p++) {
|
||||||
final PointerState ps = mPointers.get(p);
|
final PointerState ps = mPointers.get(p);
|
||||||
ps.mXs.clear();
|
ps.mXs.clear();
|
||||||
ps.mYs.clear();
|
ps.mYs.clear();
|
||||||
ps.mVelocity = VelocityTracker.obtain();
|
|
||||||
ps.mCurDown = false;
|
ps.mCurDown = false;
|
||||||
}
|
}
|
||||||
mPointers.get(0).mCurDown = true;
|
mPointers.get(0).mCurDown = true;
|
||||||
@@ -256,12 +257,10 @@ public class PointerLocationView extends View {
|
|||||||
final int id = event.getPointerId(index);
|
final int id = event.getPointerId(index);
|
||||||
while (NP <= id) {
|
while (NP <= id) {
|
||||||
PointerState ps = new PointerState();
|
PointerState ps = new PointerState();
|
||||||
ps.mVelocity = VelocityTracker.obtain();
|
|
||||||
mPointers.add(ps);
|
mPointers.add(ps);
|
||||||
NP++;
|
NP++;
|
||||||
}
|
}
|
||||||
final PointerState ps = mPointers.get(id);
|
final PointerState ps = mPointers.get(id);
|
||||||
ps.mVelocity = VelocityTracker.obtain();
|
|
||||||
ps.mCurDown = true;
|
ps.mCurDown = true;
|
||||||
if (mPrintCoords) {
|
if (mPrintCoords) {
|
||||||
Log.i("Pointer", "Pointer " + (id+1) + ": DOWN");
|
Log.i("Pointer", "Pointer " + (id+1) + ": DOWN");
|
||||||
@@ -276,12 +275,13 @@ public class PointerLocationView extends View {
|
|||||||
if (mMaxNumPointers < mCurNumPointers) {
|
if (mMaxNumPointers < mCurNumPointers) {
|
||||||
mMaxNumPointers = mCurNumPointers;
|
mMaxNumPointers = mCurNumPointers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mVelocity.addMovement(event);
|
||||||
|
mVelocity.computeCurrentVelocity(1);
|
||||||
|
|
||||||
for (int i=0; i<NI; i++) {
|
for (int i=0; i<NI; i++) {
|
||||||
final int id = event.getPointerId(i);
|
final int id = event.getPointerId(i);
|
||||||
final PointerState ps = mPointers.get(id);
|
final PointerState ps = mPointers.get(id);
|
||||||
ps.mVelocity.addMovement(event);
|
|
||||||
ps.mVelocity.computeCurrentVelocity(1);
|
|
||||||
final int N = event.getHistorySize();
|
final int N = event.getHistorySize();
|
||||||
for (int j=0; j<N; j++) {
|
for (int j=0; j<N; j++) {
|
||||||
if (mPrintCoords) {
|
if (mPrintCoords) {
|
||||||
@@ -309,6 +309,8 @@ public class PointerLocationView extends View {
|
|||||||
ps.mCurPressure = event.getPressure(i);
|
ps.mCurPressure = event.getPressure(i);
|
||||||
ps.mCurSize = event.getSize(i);
|
ps.mCurSize = event.getSize(i);
|
||||||
ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3));
|
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) {
|
if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
|
||||||
|
|||||||
@@ -565,7 +565,9 @@ protected:
|
|||||||
|
|
||||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||||
pointers[i] = other.pointers[i];
|
pointers[i] = other.pointers[i];
|
||||||
idToIndex[i] = other.idToIndex[i];
|
|
||||||
|
int id = pointers[i].id;
|
||||||
|
idToIndex[id] = other.idToIndex[id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user