Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d46ce24 authored by Chris Tate's avatar Chris Tate Committed by Android (Google) Code Review
Browse files

Merge "Enhanced VelocityTracker for > 5 pointers and fixed bugs." into gingerbread

parents 9abce39a 9e2ad36b
Loading
Loading
Loading
Loading
+42 −6
Original line number Diff line number Diff line
@@ -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);
@@ -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);
                    }
                }
            }
        }
    }
+17 −24
Original line number Diff line number Diff line
@@ -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;
+168 −85
Original line number Diff line number Diff line
@@ -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(
@@ -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;

@@ -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;
    }
    
    /**
@@ -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;
    }

    /**
@@ -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);
            }
        }
    }
    
@@ -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;
    }
    
    /**
@@ -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;
    }
    
    /**
@@ -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;
    }
    
    /**
@@ -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;
    }
}
+19 −17
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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) {
@@ -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);
@@ -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);
                    }
                }
            }
        }
@@ -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;
@@ -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");
@@ -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) {
@@ -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) {
+3 −1
Original line number Diff line number Diff line
@@ -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];
            }
        }