PointerLocationView.java revision 70825161b5bf51ed48319e142751a9c88b104994
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;
26c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brownimport android.view.KeyEvent;
2790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.MotionEvent;
2890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.VelocityTracker;
2990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.View;
3090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.ViewConfiguration;
31cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brownimport android.view.MotionEvent.PointerCoords;
3290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.ArrayList;
3490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornpublic class PointerLocationView extends View {
368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final String TAG = "Pointer";
378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
3890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public static class PointerState {
398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Trace of previous points.
408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceX = new float[32];
418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceY = new float[32];
428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mTraceCount;
438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // True if the pointer is down.
4590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private boolean mCurDown;
468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent coordinates.
48cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown        private PointerCoords mCoords = new PointerCoords();
4965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        private int mToolType;
508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent velocity.
529e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mXVelocity;
539e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mYVelocity;
54b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
55b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        // Position estimator.
56b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
57b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void clearTrace() {
598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount = 0;
608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void addTrace(float x, float y) {
638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int traceCapacity = mTraceX.length;
648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (mTraceCount == traceCapacity) {
658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                traceCapacity *= 2;
668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceX = new float[traceCapacity];
678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceX = newTraceX;
698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceY = new float[traceCapacity];
718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceY = newTraceY;
738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceX[mTraceCount] = x;
768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceY[mTraceCount] = y;
778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount += 1;
788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
8090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
81b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_PAST_POINTS = 4;
82b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_FUTURE_POINTS = 2;
83b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final float ESTIMATE_INTERVAL = 0.02f;
84b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
8590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
8690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
8790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
8890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
8990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
9090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
9190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
9290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
9390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
9490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
9590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
9690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
97d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    private int mActivePointerId;
988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
9965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private final PointerCoords mTempCoords = new PointerCoords();
10090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1019e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown    private final VelocityTracker mVelocity;
1029e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final FasterStringBuilder mText = new FasterStringBuilder();
1048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
10690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
10790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
10890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
109c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        setFocusableInTouchMode(true);
110c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
11790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
12390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
12490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
12590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
12790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
12890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
12990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
13090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
13190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
13290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
13390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
13490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
13590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
13690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
13790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
13890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
139d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown        mActivePointerId = 0;
1409e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1419e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        mVelocity = VelocityTracker.obtain();
1428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        logInputDeviceCapabilities();
1448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
1458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void logInputDeviceCapabilities() {
1478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        int[] deviceIds = InputDevice.getDeviceIds();
1488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        for (int i = 0; i < deviceIds.length; i++) {
1498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            InputDevice device = InputDevice.getDevice(deviceIds[i]);
1508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (device != null) {
1518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                Log.i(TAG, device.toString());
1528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
1538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
15990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
16090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
16190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
16290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
16390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
16490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
16590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
16690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
16790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
16890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
16990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
17090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
17190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
17290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
1738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
1758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // angles less than or greater than 0 radians rotate the major axis left or right.
1768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private RectF mReusableOvalRect = new RectF();
1778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
1788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            float angle, Paint paint) {
1798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.save(Canvas.MATRIX_SAVE_FLAG);
1805068ad8d2396d6d7bfbdb1c2c3fe57104744f1f9Jeff Brown        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
1818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.left = x - minor / 2;
1828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.right = x + minor / 2;
1838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.top = y - major / 2;
1848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.bottom = y + major / 2;
1858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.drawOval(mReusableOvalRect, paint);
1868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.restore();
1878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
19170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int w = getWidth();
19270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int itemW = w/7;
19370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int base = -mTextMetrics.ascent+1;
19470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int bottom = mHeaderBottom;
19570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
19670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NP = mPointers.size();
19770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
19870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Labels
19970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (mActivePointerId >= 0) {
20070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(mActivePointerId);
20190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
20270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
20370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
20470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("P: ").append(mCurNumPointers)
20570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append(" / ").append(mMaxNumPointers)
20670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1, base, mTextPaint);
20770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
20870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
20970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if ((mCurDown && ps.mCurDown) || N == 0) {
21070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
2118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
21270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("X: ").append(ps.mCoords.x, 1)
21370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
21470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
2158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
21670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("Y: ").append(ps.mCoords.y, 1)
21770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
21870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
21970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
22070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
22170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
22270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dx) < mVC.getScaledTouchSlop()
22370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
22570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dX: ").append(dx, 1)
22670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
22770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
22870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dy) < mVC.getScaledTouchSlop()
22970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
23170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dY: ").append(dy, 1)
23270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
23390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
23470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
23570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
23670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
23770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Xv: ").append(ps.mXVelocity, 3)
23870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 3, base, mTextPaint);
23970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
24070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
24170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
24270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Yv: ").append(ps.mYVelocity, 3)
24370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 4, base, mTextPaint);
24470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
24570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
24670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
24770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
24870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
24970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Prs: ").append(ps.mCoords.pressure, 2)
25070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 5, base, mTextPaint);
25170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
25270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
25370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
25470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
25570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
25670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Size: ").append(ps.mCoords.size, 2)
25770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 6, base, mTextPaint);
25870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
25970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
26070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Pointer trace.
26170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int p = 0; p < NP; p++) {
26270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(p);
26370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
26470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            // Draw path.
26570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
26670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            float lastX = 0, lastY = 0;
26770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean haveLast = false;
26870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean drawn = false;
26970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mPaint.setARGB(255, 128, 255, 255);
27070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            for (int i=0; i < N; i++) {
27170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float x = ps.mTraceX[i];
27270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float y = ps.mTraceY[i];
27370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (Float.isNaN(x)) {
27470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    haveLast = false;
27570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    continue;
27690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
27770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (haveLast) {
27870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
27970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawPoint(lastX, lastY, mPaint);
28070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    drawn = true;
2818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
28270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastX = x;
28370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastY = y;
28470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                haveLast = true;
28570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
28670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
28770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (drawn) {
28870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw movement estimate curve.
28970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(128, 128, 0, 128);
29070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
29170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
29270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
29370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
29470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
29570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lx, ly, x, y, mPaint);
29670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    lx = x;
29770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ly = y;
29870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
29970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
30070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw velocity vector.
30170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, 255, 64, 128);
30270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float xVel = ps.mXVelocity * (1000 / 60);
30370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float yVel = ps.mYVelocity * (1000 / 60);
30470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
30570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
30670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
30770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mCurDown && ps.mCurDown) {
30870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw crosshairs.
30970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
31070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
31170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
31270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current point.
31370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                int pressureLevel = (int)(ps.mCoords.pressure * 255);
31470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
31570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
31670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
31770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current touch ellipse.
31870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
31970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
32070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
32170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
32270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current tool ellipse.
32370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
32470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
32570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
32670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
32770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the orientation arrow.
32870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float arrowSize = ps.mCoords.toolMajor * 0.7f;
32970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (arrowSize < 20) {
33070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    arrowSize = 20;
33170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
33270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 0);
33370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
33470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
33570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
33670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
33770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
33870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
33970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show full circle orientation.
34070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
34170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
34270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
34370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
34470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                } else {
34570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show half circle orientation.
34670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(
34770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x - orientationVectorX,
34870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y - orientationVectorY,
34970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
35070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
35170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
35290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
35370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
35470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the tilt point along the orientation arrow.
35570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float tiltScale = (float) Math.sin(
35670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
35770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawCircle(
35870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.x + orientationVectorX * tiltScale,
35970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.y + orientationVectorY * tiltScale,
36070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        3.0f, mPaint);
36190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
36290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
36390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
36465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
36565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logMotionEvent(String type, MotionEvent event) {
36665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int action = event.getAction();
36765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int N = event.getHistorySize();
36865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int NI = event.getPointerCount();
36965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
37065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            for (int i = 0; i < NI; i++) {
37165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final int id = event.getPointerId(i);
37265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                event.getHistoricalPointerCoords(i, historyPos, mTempCoords);
37365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                logCoords(type, action, i, mTempCoords, id,
37465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                        event.getToolType(i), event.getButtonState());
37565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            }
37665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
37765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int i = 0; i < NI; i++) {
37865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            final int id = event.getPointerId(i);
37965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            event.getPointerCoords(i, mTempCoords);
38065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logCoords(type, action, i, mTempCoords, id,
38165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                    event.getToolType(i), event.getButtonState());
38265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
38365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    }
38465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
38565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logCoords(String type, int action, int index,
38665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            MotionEvent.PointerCoords coords, int id, int toolType, int buttonState) {
38733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        final String prefix;
38833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        switch (action & MotionEvent.ACTION_MASK) {
38933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_DOWN:
39033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "DOWN";
39133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
39233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_UP:
39333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "UP";
39433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
39533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_MOVE:
39633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "MOVE";
39733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
39833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_CANCEL:
39933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "CANCEL";
40033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
40133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_OUTSIDE:
40233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "OUTSIDE";
40333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
40433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_DOWN:
40533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
40633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
40733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "DOWN";
40833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
40933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
41033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
41133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
41233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_UP:
41333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
41433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
41533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "UP";
41633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
41733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
41833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
41933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
42033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_HOVER_MOVE:
42133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "HOVER MOVE";
42233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
423a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_ENTER:
424a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER ENTER";
425a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
426a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_EXIT:
427a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER EXIT";
428a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
42933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_SCROLL:
43033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "SCROLL";
43133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
43233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            default:
43333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = Integer.toString(action);
43433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
43533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
43633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
4378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, mText.clear()
43865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(type).append(" id ").append(id + 1)
43933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(": ")
44033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(prefix)
44133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
4428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(") Pressure=").append(coords.pressure, 3)
4438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Size=").append(coords.size, 3)
4448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMajor=").append(coords.touchMajor, 3)
4458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMinor=").append(coords.touchMinor, 3)
4468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMajor=").append(coords.toolMajor, 3)
4478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMinor=").append(coords.toolMinor, 3)
4488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
4496f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append("deg")
45065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(" Tilt=").append((float)(
45165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                        coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
45265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append("deg")
45380fd47ce75253dcdc2cfa85d7a3f42634b923a47Jeff Brown                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
4546f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
4556f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
456fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
457fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
4586f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .toString());
4598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
46090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
46133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public void addPointerEvent(MotionEvent event) {
46270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int action = event.getAction();
46370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        int NP = mPointers.size();
46433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
46570825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_DOWN
46670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
46770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
46870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
46970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (action == MotionEvent.ACTION_DOWN) {
47070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int p=0; p<NP; p++) {
47170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    final PointerState ps = mPointers.get(p);
47270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.clearTrace();
47370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.mCurDown = false;
47433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
47570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = true;
47670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
47770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = 0;
47870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mVelocity.clear();
47970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
48033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
48170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mCurNumPointers += 1;
48270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mMaxNumPointers < mCurNumPointers) {
48370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = mCurNumPointers;
48490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
485cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown
48670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
48770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            while (NP <= id) {
48870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                PointerState ps = new PointerState();
48970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPointers.add(ps);
49070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                NP++;
49170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
49270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
49370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mActivePointerId < 0 ||
49470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    !mPointers.get(mActivePointerId).mCurDown) {
49570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mActivePointerId = id;
49633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            }
49770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
49870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
49970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = true;
50070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
50170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
50270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NI = event.getPointerCount();
50370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
50470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.addMovement(event);
50570825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.computeCurrentVelocity(1);
50670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
50770825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int N = event.getHistorySize();
50870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
50933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            for (int i = 0; i < NI; i++) {
51033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final int id = event.getPointerId(i);
51133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final PointerState ps = mCurDown ? mPointers.get(id) : null;
51265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
51370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                event.getHistoricalPointerCoords(i, historyPos, coords);
51490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
51565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                    logCoords("Pointer", action, i, coords, id,
516fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                            event.getToolType(i), event.getButtonState());
517cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                }
518cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                if (ps != null) {
519cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    ps.addTrace(coords.x, coords.y);
52090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
52190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
52270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
52370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int i = 0; i < NI; i++) {
52470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(i);
52570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mCurDown ? mPointers.get(id) : null;
52670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
52770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            event.getPointerCoords(i, coords);
52870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mPrintCoords) {
52970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                logCoords("Pointer", action, i, coords, id,
53070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        event.getToolType(i), event.getButtonState());
53170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
53270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (ps != null) {
53370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.addTrace(coords.x, coords.y);
53470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mXVelocity = mVelocity.getXVelocity(id);
53570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mYVelocity = mVelocity.getYVelocity(id);
53670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mVelocity.getEstimator(id, -1, -1, ps.mEstimator);
53770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mToolType = event.getToolType(i);
53870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
53970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
54070825161b5bf51ed48319e142751a9c88b104994Jeff Brown
54170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_UP
54270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || action == MotionEvent.ACTION_CANCEL
54370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
54470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
54570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
54670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
54770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
54870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
54970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = false;
55033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
5518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_UP
55270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    || action == MotionEvent.ACTION_CANCEL) {
55370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = false;
55470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
55570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
55670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers -= 1;
55770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (mActivePointerId == id) {
55870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
55990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
56070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.addTrace(Float.NaN, Float.NaN);
56190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
56290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
56370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
56470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        invalidate();
56590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
56690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
56790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
56890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
56933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        addPointerEvent(event);
570c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
571c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
572c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            requestFocus();
573c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
57490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
57590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
5767d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
5777d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    @Override
57833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public boolean onGenericMotionEvent(MotionEvent event) {
579c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        final int source = event.getSource();
580c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
58133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            addPointerEvent(event);
582c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
58365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Joystick", event);
584c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
58565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Position", event);
586c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else {
58765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Generic", event);
588c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
589c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
590c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
591c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
592c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
593c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyDown(int keyCode, KeyEvent event) {
594c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
595c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            final int repeatCount = event.getRepeatCount();
596c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            if (repeatCount == 0) {
597c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Down: " + event);
598c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            } else {
599c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
600c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            }
60133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            return true;
60233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
603c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyDown(keyCode, event);
604c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
605c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
606c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
607c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyUp(int keyCode, KeyEvent event) {
608c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
609c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Key Up: " + event);
610c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            return true;
611c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
612c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyUp(keyCode, event);
613c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
614c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
615c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    private static boolean shouldLogKey(int keyCode) {
616c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        switch (keyCode) {
617c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_UP:
618c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_DOWN:
619c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_LEFT:
620c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_RIGHT:
621c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
622c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return true;
623c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            default:
624c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return KeyEvent.isGamepadButton(keyCode)
625c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                    || KeyEvent.isModifierKey(keyCode);
626c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
62733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    }
62833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
62933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    @Override
6307d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    public boolean onTrackballEvent(MotionEvent event) {
63165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        logMotionEvent("Trackball", event);
632c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
6337d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    }
6347d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
6358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // HACK
6368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // A quick and dirty string builder implementation optimized for GC.
637d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // Using String.format causes the application grind to a halt when
638d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // more than a couple of pointers are down due to the number of
639d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // temporary objects allocated while formatting strings for drawing or logging.
6408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final class FasterStringBuilder {
6418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private char[] mChars;
6428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mLength;
6438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder() {
6458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mChars = new char[64];
6468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder clear() {
6498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = 0;
6508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
6518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(String value) {
6548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int valueLength = value.length();
6558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int index = reserve(valueLength);
6568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value.getChars(0, valueLength, mChars, index);
6578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength += valueLength;
6588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
6598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value) {
6628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return append(value, 0);
6638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value, int zeroPadWidth) {
6668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final boolean negative = value < 0;
6678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
6688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = - value;
6698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (value < 0) {
6708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    append("-2147483648");
6718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    return this;
6728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
6738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int index = reserve(11);
6768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] chars = mChars;
6778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (value == 0) {
6798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '0';
6808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mLength += 1;
6818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                return this;
6828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
6858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '-';
6868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int divisor = 1000000000;
6898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int numberWidth = 10;
6908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            while (value < divisor) {
6918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
6928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                numberWidth -= 1;
6938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (numberWidth < zeroPadWidth) {
6948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    chars[index++] = '0';
6958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
6968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            do {
6998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                int digit = value / divisor;
7008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= digit * divisor;
7018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
7028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = (char) (digit + '0');
7038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            } while (divisor != 0);
7048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = index;
7068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(float value, int precision) {
7108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int scale = 1;
7118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int i = 0; i < precision; i++) {
7128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                scale *= 10;
7138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
7148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value = (float) (Math.rint(value * scale) / scale);
7158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            append((int) value);
7178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (precision != 0) {
7198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append(".");
7208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = Math.abs(value);
7218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= Math.floor(value);
7228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append((int) (value * scale), precision);
7238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
7248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        @Override
7298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public String toString() {
7308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return new String(mChars, 0, mLength);
7318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int reserve(int length) {
7348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldLength = mLength;
7358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int newLength = mLength + length;
7368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] oldChars = mChars;
7378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldCapacity = oldChars.length;
7388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (newLength > oldCapacity) {
7398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int newCapacity = oldCapacity * 2;
7408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final char[] newChars = new char[newCapacity];
7418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
7428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mChars = newChars;
7438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
7448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return oldLength;
7458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
74790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
748