PointerLocationView.java revision f8d0f095e34f8d661ca5b7d555d8610272099bff
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;
2290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.graphics.Paint.FontMetricsInt;
2390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.util.Log;
2490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.MotionEvent;
2590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.VelocityTracker;
2690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.View;
2790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.ViewConfiguration;
2890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.ArrayList;
3090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornpublic class PointerLocationView extends View {
3290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public static class PointerState {
3390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private final ArrayList<Float> mXs = new ArrayList<Float>();
3490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private final ArrayList<Float> mYs = new ArrayList<Float>();
3590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private boolean mCurDown;
3690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private int mCurX;
3790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private int mCurY;
3890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private float mCurPressure;
3990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private float mCurSize;
4090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private int mCurWidth;
4190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private VelocityTracker mVelocity;
4290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
4390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
4490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
4590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
4690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
4790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
4890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
4990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
5090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
5190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
5290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
5390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
5490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
5590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
5690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ArrayList<PointerState> mPointers
5790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn             = new ArrayList<PointerState>();
5890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
5990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
6090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
6190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
6290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
6390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
6490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
6590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
6690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
6790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
6890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
6990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
7090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
7190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
7290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
7390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
7490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
7590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
7690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
7790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
7890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
7990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
8090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
8190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
8290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
8390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
8490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
8590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
8690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
8790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
8890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
8990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
9090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        ps.mVelocity = VelocityTracker.obtain();
9190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
9290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
9390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
9490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
9590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
9690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
9790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
9890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
9990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
10090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
10190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
10290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
10390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
10490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
10690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
10790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
10890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
10990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
11090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int w = getWidth();
11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int itemW = w/7;
11790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int base = -mTextMetrics.ascent+1;
11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int bottom = mHeaderBottom;
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int NP = mPointers.size();
12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if (NP > 0) {
12390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(0);
12490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
12590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers,
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        1, base, mTextPaint);
12790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final int N = ps.mXs.size();
12990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if ((mCurDown && ps.mCurDown) || N == 0) {
13090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
13190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint);
13290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
13390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint);
13490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                } else {
13590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    float dx = ps.mXs.get(N-1) - ps.mXs.get(0);
13690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    float dy = ps.mYs.get(N-1) - ps.mYs.get(0);
13790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
13890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dx) < mVC.getScaledTouchSlop()
13990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
14090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint);
14190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
14290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dy) < mVC.getScaledTouchSlop()
14390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
14490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint);
14590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000);
14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint);
15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000);
15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint);
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1,
15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5,
15990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        base, mTextPaint);
16090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
16190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
16290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1,
16390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
16490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6,
16590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        base, mTextPaint);
16690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
16790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
16890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            for (int p=0; p<NP; p++) {
16990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(p);
17090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
17190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mCurDown && ps.mCurDown) {
17290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint);
17390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint);
17490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    int pressureLevel = (int)(ps.mCurPressure*255);
17590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel);
17690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint);
17790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint);
17890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
17990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
18090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            for (int p=0; p<NP; p++) {
18290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(p);
18390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final int N = ps.mXs.size();
18590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                float lastX=0, lastY=0;
18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean haveLast = false;
18790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean drawn = false;
18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mPaint.setARGB(255, 128, 255, 255);
18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                for (int i=0; i<N; i++) {
19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    float x = ps.mXs.get(i);
19190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    float y = ps.mYs.get(i);
19290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (Float.isNaN(x)) {
19390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        haveLast = false;
19490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        continue;
19590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
19690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (haveLast) {
19790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);
19890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawPoint(lastX, lastY, mPaint);
19990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        drawn = true;
20090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
20190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastX = x;
20290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastY = y;
20390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    haveLast = true;
20490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
20590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
20690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (drawn) {
20790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (ps.mVelocity != null) {
20890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        mPaint.setARGB(255, 255, 64, 128);
20990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        float xVel = ps.mVelocity.getXVelocity() * (1000/60);
21090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        float yVel = ps.mVelocity.getYVelocity() * (1000/60);
21190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
21290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    } else {
21390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawPoint(lastX, lastY, mPaint);
21490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
21590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
21690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
21790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
21890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
21990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void addTouchEvent(MotionEvent event) {
22190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
22290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            int action = event.getAction();
22390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action)
22590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //        + " pointers=" + event.getPointerCount());
22690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            int NP = mPointers.size();
22890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //mRect.set(0, 0, getWidth(), mHeaderBottom+1);
23090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //invalidate(mRect);
23190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //if (mCurDown) {
23290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
23390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
23490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //} else {
23590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.setEmpty();
23690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //}
23790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if (action == MotionEvent.ACTION_DOWN) {
23890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                for (int p=0; p<NP; p++) {
23990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    final PointerState ps = mPointers.get(p);
24090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mXs.clear();
24190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mYs.clear();
24290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mVelocity = VelocityTracker.obtain();
24390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mCurDown = false;
24490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
24590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mPointers.get(0).mCurDown = true;
24690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mMaxNumPointers = 0;
24790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
24890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    Log.i("Pointer", "Pointer 1: DOWN");
24990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
25090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
25190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
25290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
253b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK)
254b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
255b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
25690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                while (NP <= id) {
25790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    PointerState ps = new PointerState();
25890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mVelocity = VelocityTracker.obtain();
25990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    mPointers.add(ps);
26090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    NP++;
26190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
26290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
26390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mVelocity = VelocityTracker.obtain();
26490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = true;
26590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
26690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    Log.i("Pointer", "Pointer " + (id+1) + ": DOWN");
26790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
26890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
26990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
27090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int NI = event.getPointerCount();
27190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
27290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            mCurDown = action != MotionEvent.ACTION_UP
27390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    && action != MotionEvent.ACTION_CANCEL;
27490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            mCurNumPointers = mCurDown ? NI : 0;
27590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if (mMaxNumPointers < mCurNumPointers) {
27690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mMaxNumPointers = mCurNumPointers;
27790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
27890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
27990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            for (int i=0; i<NI; i++) {
280b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(i);
281b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final PointerState ps = mPointers.get(id);
28290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mVelocity.addMovement(event);
28390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mVelocity.computeCurrentVelocity(1);
28490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final int N = event.getHistorySize();
28590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                for (int j=0; j<N; j++) {
28690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (mPrintCoords) {
287b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                        Log.i("Pointer", "Pointer " + (id+1) + ": ("
28890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                                + event.getHistoricalX(i, j)
28990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                                + ", " + event.getHistoricalY(i, j) + ")"
29090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                                + " Prs=" + event.getHistoricalPressure(i, j)
29190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                                + " Size=" + event.getHistoricalSize(i, j));
29290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
29390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mXs.add(event.getHistoricalX(i, j));
29490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    ps.mYs.add(event.getHistoricalY(i, j));
29590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
29690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
297b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                    Log.i("Pointer", "Pointer " + (id+1) + ": ("
29890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            + event.getX(i) + ", " + event.getY(i) + ")"
29990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            + " Prs=" + event.getPressure(i)
30090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            + " Size=" + event.getSize(i));
30190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
30290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mXs.add(event.getX(i));
30390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mYs.add(event.getY(i));
30490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurX = (int)event.getX(i);
30590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurY = (int)event.getY(i);
30690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX
30790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                //        + "," + ps.mCurY + ")");
30890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurPressure = event.getPressure(i);
30990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurSize = event.getSize(i);
31090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3));
31190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
31290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
31390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
314b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK)
315b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
316b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
31790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
31890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mXs.add(Float.NaN);
31990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mYs.add(Float.NaN);
32090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = false;
32190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
32290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    Log.i("Pointer", "Pointer " + (id+1) + ": UP");
32390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
32490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
32590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
32690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            if (action == MotionEvent.ACTION_UP) {
32790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                for (int i=0; i<NI; i++) {
328b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                    final int id = event.getPointerId(i);
329b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                    final PointerState ps = mPointers.get(id);
33090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (ps.mCurDown) {
33190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        ps.mCurDown = false;
33290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        if (mPrintCoords) {
333b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                            Log.i("Pointer", "Pointer " + (id+1) + ": UP");
33490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        }
33590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
33690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
33790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
33890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
33990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //if (mCurDown) {
34090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //    mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
34190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
34290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //}
34390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            //invalidate(mRect);
34490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            postInvalidate();
34590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
34690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
34790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
34890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
34990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
35090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        addTouchEvent(event);
35190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
35290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
35390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
354