PointerLocationView.java revision d1e0c371a651f024b3de62a686c56e369e3f361e
1f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root/*
2f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Copyright (C) 2010 The Android Open Source Project
3f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root *
4f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * you may not use this file except in compliance with the License.
6f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * You may obtain a copy of the License at
7f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root *
8f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root *
10f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Unless required by applicable law or agreed to in writing, software
11f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * See the License for the specific language governing permissions and
14f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * limitations under the License.
15f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root */
16f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root
17829778843cf459384841f9f4ecafe862b6228d6eDianne Hackbornpackage com.android.internal.widget;
1890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.content.Context;
2090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.graphics.Canvas;
2190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.graphics.Paint;
228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownimport android.graphics.RectF;
2390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.graphics.Paint.FontMetricsInt;
2490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.util.Log;
258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownimport android.view.InputDevice;
2690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.MotionEvent;
2790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.VelocityTracker;
2890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.View;
2990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.ViewConfiguration;
3090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.ArrayList;
3290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornpublic class PointerLocationView extends View {
348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final String TAG = "Pointer";
358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
3690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public static class PointerState {
378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Trace of previous points.
388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceX = new float[32];
398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceY = new float[32];
408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mTraceCount;
418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // True if the pointer is down.
4390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private boolean mCurDown;
448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent coordinates.
468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private MotionEvent.PointerCoords mCoords = new MotionEvent.PointerCoords();
478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent velocity.
499e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mXVelocity;
509e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mYVelocity;
518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void clearTrace() {
538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount = 0;
548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void addTrace(float x, float y) {
578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int traceCapacity = mTraceX.length;
588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (mTraceCount == traceCapacity) {
598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                traceCapacity *= 2;
608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceX = new float[traceCapacity];
618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceX = newTraceX;
638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceY = new float[traceCapacity];
658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceY = newTraceY;
678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceX[mTraceCount] = x;
708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceY[mTraceCount] = y;
718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount += 1;
728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
7490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
7590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
7690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
7790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
7890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
7990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
8090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
8190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
8290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
8390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
8490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
8590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
8690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
87d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    private int mActivePointerId;
888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
8990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
909e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown    private final VelocityTracker mVelocity;
919e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final FasterStringBuilder mText = new FasterStringBuilder();
938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
9490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
9590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
9690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
9790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
987d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn        setFocusable(true);
9990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
10090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
10190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
10290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
10390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
10490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
10690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
10790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
10890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
10990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
11090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
11790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
12390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
12490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
127d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown        mActivePointerId = 0;
1289e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1299e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        mVelocity = VelocityTracker.obtain();
1308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        logInputDeviceCapabilities();
1328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
1338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void logInputDeviceCapabilities() {
1358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        int[] deviceIds = InputDevice.getDeviceIds();
1368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        for (int i = 0; i < deviceIds.length; i++) {
1378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            InputDevice device = InputDevice.getDevice(deviceIds[i]);
1388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (device != null) {
1398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                Log.i(TAG, device.toString());
1408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
1418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
14290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
14390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
14490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
14590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
15990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
16090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
1618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
1638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // angles less than or greater than 0 radians rotate the major axis left or right.
1648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private RectF mReusableOvalRect = new RectF();
1658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
1668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            float angle, Paint paint) {
1678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.save(Canvas.MATRIX_SAVE_FLAG);
1688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
1698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.left = x - minor / 2;
1708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.right = x + minor / 2;
1718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.top = y - major / 2;
1728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.bottom = y + major / 2;
1738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.drawOval(mReusableOvalRect, paint);
1748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.restore();
1758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
17690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
17790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
17890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
17990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
18090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int w = getWidth();
18190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int itemW = w/7;
18290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int base = -mTextMetrics.ascent+1;
18390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int bottom = mHeaderBottom;
18490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int NP = mPointers.size();
18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            // Labels
188d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown            if (mActivePointerId >= 0) {
189d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                final PointerState ps = mPointers.get(mActivePointerId);
190d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
19190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
1928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
1938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("P: ").append(mCurNumPointers)
1948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append(" / ").append(mMaxNumPointers)
1958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1, base, mTextPaint);
196d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
1978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int N = ps.mTraceCount;
19890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if ((mCurDown && ps.mCurDown) || N == 0) {
19990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
2008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("X: ").append(ps.mCoords.x, 1)
2028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW, base, mTextPaint);
20390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
2048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("Y: ").append(ps.mCoords.y, 1)
2068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW * 2, base, mTextPaint);
20790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                } else {
2088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
2098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
21090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
21190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dx) < mVC.getScaledTouchSlop()
21290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
2138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("dX: ").append(dx, 1)
2158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW, base, mTextPaint);
21690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
21790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dy) < mVC.getScaledTouchSlop()
21890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
2198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("dY: ").append(dy, 1)
2218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW * 2, base, mTextPaint);
22290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
22390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
2258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Xv: ").append(ps.mXVelocity, 3)
2278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 3, base, mTextPaint);
22890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
2308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Yv: ").append(ps.mYVelocity, 3)
2328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 4, base, mTextPaint);
23390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
23490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
2358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
23690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
2378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Prs: ").append(ps.mCoords.pressure, 2)
2398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 5, base, mTextPaint);
24090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
24190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
2428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
24390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
2448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Size: ").append(ps.mCoords.size, 2)
2468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 6, base, mTextPaint);
24790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
24890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            // Pointer trace.
2508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int p = 0; p < NP; p++) {
25190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(p);
25290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                // Draw path.
2548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int N = ps.mTraceCount;
2558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float lastX = 0, lastY = 0;
25690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean haveLast = false;
25790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean drawn = false;
25890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mPaint.setARGB(255, 128, 255, 255);
2598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                for (int i=0; i < N; i++) {
2608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float x = ps.mTraceX[i];
2618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float y = ps.mTraceY[i];
26290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (Float.isNaN(x)) {
26390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        haveLast = false;
26490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        continue;
26590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
26690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (haveLast) {
26790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);
26890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawPoint(lastX, lastY, mPaint);
26990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        drawn = true;
27090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
27190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastX = x;
27290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastY = y;
27390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    haveLast = true;
27490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
27590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                // Draw velocity vector.
27790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (drawn) {
2789e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown                    mPaint.setARGB(255, 255, 64, 128);
2798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float xVel = ps.mXVelocity * (1000 / 60);
2808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float yVel = ps.mYVelocity * (1000 / 60);
2818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
2828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
2838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (mCurDown && ps.mCurDown) {
2858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw crosshairs.
2868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
2878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
2888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current point.
2908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    int pressureLevel = (int)(ps.mCoords.pressure * 255);
2918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
2928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
2938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current touch ellipse.
2958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
2968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
2978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
2988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current tool ellipse.
3008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
3018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
3028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
30390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
30490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
30590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
30690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
3078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
3088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void logPointerCoords(MotionEvent.PointerCoords coords, int id) {
3098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, mText.clear()
3108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append("Pointer ").append(id + 1)
3118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(": (").append(coords.x, 3).append(", ").append(coords.y, 3)
3128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(") Pressure=").append(coords.pressure, 3)
3138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Size=").append(coords.size, 3)
3148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMajor=").append(coords.touchMajor, 3)
3158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMinor=").append(coords.touchMinor, 3)
3168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMajor=").append(coords.toolMajor, 3)
3178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMinor=").append(coords.toolMinor, 3)
3188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
3198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append("deg").toString());
3208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
32190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
32290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void addTouchEvent(MotionEvent event) {
32390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
32490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            int action = event.getAction();
32590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            //Log.i(TAG, "Motion: action=0x" + Integer.toHexString(action)
32790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //        + " pointers=" + event.getPointerCount());
32890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
32990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            int NP = mPointers.size();
33090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
33190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //mRect.set(0, 0, getWidth(), mHeaderBottom+1);
33290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //invalidate(mRect);
33390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //if (mCurDown) {
33490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
33590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
33690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //} else {
33790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.setEmpty();
33890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //}
3398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_DOWN
3408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
3418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
3428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
3438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (action == MotionEvent.ACTION_DOWN) {
3448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    for (int p=0; p<NP; p++) {
3458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        final PointerState ps = mPointers.get(p);
3468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        ps.clearTrace();
3478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        ps.mCurDown = false;
3488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    }
3498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mCurDown = true;
3508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mMaxNumPointers = 0;
3518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mVelocity.clear();
35290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
3538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
354b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
35590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                while (NP <= id) {
35690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    PointerState ps = new PointerState();
35790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    mPointers.add(ps);
35890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    NP++;
35990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
3608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
361d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                if (mActivePointerId < 0 ||
362d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                        ! mPointers.get(mActivePointerId).mCurDown) {
363d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    mActivePointerId = id;
364d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                }
365d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
36690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
36790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = true;
36890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
3698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    Log.i(TAG, mText.clear().append("Pointer ")
3708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append(id + 1).append(": DOWN").toString());
37190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
37290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
37390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
37490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int NI = event.getPointerCount();
37590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
37690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            mCurDown = action != MotionEvent.ACTION_UP
37790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    && action != MotionEvent.ACTION_CANCEL;
37890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            mCurNumPointers = mCurDown ? NI : 0;
37990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if (mMaxNumPointers < mCurNumPointers) {
38090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mMaxNumPointers = mCurNumPointers;
38190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
3829e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
3839e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown            mVelocity.addMovement(event);
3849e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown            mVelocity.computeCurrentVelocity(1);
38590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
38690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            for (int i=0; i<NI; i++) {
387b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(i);
388b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final PointerState ps = mPointers.get(id);
38990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final int N = event.getHistorySize();
39090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                for (int j=0; j<N; j++) {
3918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    event.getHistoricalPointerCoords(i, j, ps.mCoords);
39290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (mPrintCoords) {
3938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        logPointerCoords(ps.mCoords, id);
39490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
3958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    ps.addTrace(event.getHistoricalX(i, j), event.getHistoricalY(i, j));
39690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
3978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                event.getPointerCoords(i, ps.mCoords);
39890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
3998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    logPointerCoords(ps.mCoords, id);
40090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
4018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                ps.addTrace(ps.mCoords.x, ps.mCoords.y);
4029e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown                ps.mXVelocity = mVelocity.getXVelocity(id);
4039e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown                ps.mYVelocity = mVelocity.getYVelocity(id);
40490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
40590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
4068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_UP
407d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    || action == MotionEvent.ACTION_CANCEL
4088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
4098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
4108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
4118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
412b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
41390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
41490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = false;
41590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
4168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    Log.i(TAG, mText.clear().append("Pointer ")
4178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append(id + 1).append(": UP").toString());
41890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
419d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
420d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                if (action == MotionEvent.ACTION_UP
421d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                        || action == MotionEvent.ACTION_CANCEL) {
4228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mCurDown = false;
4238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                } else {
424d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    if (mActivePointerId == id) {
425d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                        mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
426d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    }
4278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    ps.addTrace(Float.NaN, Float.NaN);
42890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
42990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
43090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
43190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //if (mCurDown) {
43290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
43390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
43490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //}
43590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //invalidate(mRect);
43690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            postInvalidate();
43790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
43890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
43990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
44090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
44190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
44290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        addTouchEvent(event);
44390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
44490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
4457d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
4467d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    @Override
4477d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    public boolean onTrackballEvent(MotionEvent event) {
4488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, "Trackball: " + event);
4497d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn        return super.onTrackballEvent(event);
4507d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    }
4517d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
4528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // HACK
4538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // A quick and dirty string builder implementation optimized for GC.
454d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // Using String.format causes the application grind to a halt when
455d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // more than a couple of pointers are down due to the number of
456d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // temporary objects allocated while formatting strings for drawing or logging.
4578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final class FasterStringBuilder {
4588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private char[] mChars;
4598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mLength;
4608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder() {
4628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mChars = new char[64];
4638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
4648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder clear() {
4668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = 0;
4678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
4688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
4698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(String value) {
4718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int valueLength = value.length();
4728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int index = reserve(valueLength);
4738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value.getChars(0, valueLength, mChars, index);
4748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength += valueLength;
4758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
4768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
4778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value) {
4798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return append(value, 0);
4808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
4818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value, int zeroPadWidth) {
4838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final boolean negative = value < 0;
4848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
4858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = - value;
4868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (value < 0) {
4878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    append("-2147483648");
4888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    return this;
4898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
4908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
4918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int index = reserve(11);
4938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] chars = mChars;
4948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
4958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (value == 0) {
4968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '0';
4978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mLength += 1;
4988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                return this;
4998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
5028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '-';
5038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int divisor = 1000000000;
5068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int numberWidth = 10;
5078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            while (value < divisor) {
5088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
5098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                numberWidth -= 1;
5108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (numberWidth < zeroPadWidth) {
5118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    chars[index++] = '0';
5128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
5138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            do {
5168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                int digit = value / divisor;
5178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= digit * divisor;
5188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
5198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = (char) (digit + '0');
5208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            } while (divisor != 0);
5218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = index;
5238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
5248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(float value, int precision) {
5278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int scale = 1;
5288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int i = 0; i < precision; i++) {
5298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                scale *= 10;
5308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value = (float) (Math.rint(value * scale) / scale);
5328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            append((int) value);
5348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (precision != 0) {
5368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append(".");
5378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = Math.abs(value);
5388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= Math.floor(value);
5398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append((int) (value * scale), precision);
5408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
5438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        @Override
5468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public String toString() {
5478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return new String(mChars, 0, mLength);
5488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int reserve(int length) {
5518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldLength = mLength;
5528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int newLength = mLength + length;
5538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] oldChars = mChars;
5548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldCapacity = oldChars.length;
5558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (newLength > oldCapacity) {
5568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int newCapacity = oldCapacity * 2;
5578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final char[] newChars = new char[newCapacity];
5588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
5598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mChars = newChars;
5608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
5618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return oldLength;
5628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
56490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
565