PointerLocationView.java revision 80fd47ce75253dcdc2cfa85d7a3f42634b923a47
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();
498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent velocity.
519e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mXVelocity;
529e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mYVelocity;
538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void clearTrace() {
558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount = 0;
568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void addTrace(float x, float y) {
598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int traceCapacity = mTraceX.length;
608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (mTraceCount == traceCapacity) {
618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                traceCapacity *= 2;
628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceX = new float[traceCapacity];
638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceX = newTraceX;
658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceY = new float[traceCapacity];
678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceY = newTraceY;
698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceX[mTraceCount] = x;
728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceY[mTraceCount] = y;
738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount += 1;
748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
7690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
7790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
7890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
7990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
8090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
8190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
8290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
8390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
8490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
8590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
8690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
8790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
8890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
89d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    private int mActivePointerId;
908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
91cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown    private final PointerCoords mHoverCoords = new PointerCoords();
9290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
939e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown    private final VelocityTracker mVelocity;
949e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final FasterStringBuilder mText = new FasterStringBuilder();
968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
9790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
9890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
9990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
10090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
101c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        setFocusableInTouchMode(true);
102c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
10390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
10490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
10690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
10790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
10890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
10990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
11090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
11790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
12390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
12490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
12590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
12790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
12890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
13090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
131d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown        mActivePointerId = 0;
1329e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1339e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        mVelocity = VelocityTracker.obtain();
1348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        logInputDeviceCapabilities();
1368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
1378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void logInputDeviceCapabilities() {
1398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        int[] deviceIds = InputDevice.getDeviceIds();
1408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        for (int i = 0; i < deviceIds.length; i++) {
1418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            InputDevice device = InputDevice.getDevice(deviceIds[i]);
1428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (device != null) {
1438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                Log.i(TAG, device.toString());
1448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
1458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
15990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
16090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
16190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
16290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
16390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
16490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
1658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
1678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // angles less than or greater than 0 radians rotate the major axis left or right.
1688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private RectF mReusableOvalRect = new RectF();
1698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
1708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            float angle, Paint paint) {
1718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.save(Canvas.MATRIX_SAVE_FLAG);
1725068ad8d2396d6d7bfbdb1c2c3fe57104744f1f9Jeff Brown        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
1738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.left = x - minor / 2;
1748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.right = x + minor / 2;
1758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.top = y - major / 2;
1768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.bottom = y + major / 2;
1778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.drawOval(mReusableOvalRect, paint);
1788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.restore();
1798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
18090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
18290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
18390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
18490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int w = getWidth();
18590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int itemW = w/7;
18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int base = -mTextMetrics.ascent+1;
18790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int bottom = mHeaderBottom;
18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            final int NP = mPointers.size();
19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            // Labels
192d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown            if (mActivePointerId >= 0) {
193d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                final PointerState ps = mPointers.get(mActivePointerId);
194d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
19590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
1968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
1978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("P: ").append(mCurNumPointers)
1988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append(" / ").append(mMaxNumPointers)
1998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1, base, mTextPaint);
200d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
2018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int N = ps.mTraceCount;
20290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if ((mCurDown && ps.mCurDown) || N == 0) {
20390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
2048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("X: ").append(ps.mCoords.x, 1)
2068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW, base, mTextPaint);
20790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
2088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("Y: ").append(ps.mCoords.y, 1)
2108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW * 2, base, mTextPaint);
21190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                } else {
2128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
2138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
21490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
21590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dx) < mVC.getScaledTouchSlop()
21690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
2178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("dX: ").append(dx, 1)
2198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW, base, mTextPaint);
22090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
22190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            Math.abs(dy) < mVC.getScaledTouchSlop()
22290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                            ? mTextBackgroundPaint : mTextLevelPaint);
2238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawText(mText.clear()
2248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .append("dY: ").append(dy, 1)
2258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            .toString(), 1 + itemW * 2, base, mTextPaint);
22690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
22790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
2298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Xv: ").append(ps.mXVelocity, 3)
2318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 3, base, mTextPaint);
23290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
23390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
2348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Yv: ").append(ps.mYVelocity, 3)
2368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 4, base, mTextPaint);
23790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
23890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
2398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
24090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
2418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Prs: ").append(ps.mCoords.pressure, 2)
2438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 5, base, mTextPaint);
24490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
24590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
2468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
24790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        bottom, mTextLevelPaint);
2488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
2498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .append("Size: ").append(ps.mCoords.size, 2)
2508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        .toString(), 1 + itemW * 6, base, mTextPaint);
25190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
25290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            // Pointer trace.
2548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int p = 0; p < NP; p++) {
25590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(p);
25690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                // Draw path.
2588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int N = ps.mTraceCount;
2598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float lastX = 0, lastY = 0;
26090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean haveLast = false;
26190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                boolean drawn = false;
26290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                mPaint.setARGB(255, 128, 255, 255);
2638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                for (int i=0; i < N; i++) {
2648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float x = ps.mTraceX[i];
2658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float y = ps.mTraceY[i];
26690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (Float.isNaN(x)) {
26790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        haveLast = false;
26890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        continue;
26990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
27090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (haveLast) {
27190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);
27290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        canvas.drawPoint(lastX, lastY, mPaint);
27390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                        drawn = true;
27490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
27590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastX = x;
27690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    lastY = y;
27790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    haveLast = true;
27890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
27990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
2808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                // Draw velocity vector.
28190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (drawn) {
2829e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown                    mPaint.setARGB(255, 255, 64, 128);
2838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float xVel = ps.mXVelocity * (1000 / 60);
2848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    float yVel = ps.mYVelocity * (1000 / 60);
2858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
2868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
2878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (mCurDown && ps.mCurDown) {
2898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw crosshairs.
2908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
2918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
2928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current point.
2948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    int pressureLevel = (int)(ps.mCoords.pressure * 255);
2958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
2968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
2978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current touch ellipse.
2998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
3008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
3018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
3028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
3038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    // Draw current tool ellipse.
3048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
3058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
3068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                            ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
307517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown
308517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                    // Draw the orientation arrow.
309517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                    mPaint.setARGB(255, pressureLevel, 255, 0);
310517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                    float orientationVectorX = (float) (Math.sin(-ps.mCoords.orientation)
311517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                            * ps.mCoords.toolMajor * 0.7);
312517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                    float orientationVectorY = (float) (Math.cos(-ps.mCoords.orientation)
313517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                            * ps.mCoords.toolMajor * 0.7);
314517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                    canvas.drawLine(
315517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                            ps.mCoords.x - orientationVectorX, ps.mCoords.y - orientationVectorY,
316517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                            ps.mCoords.x + orientationVectorX, ps.mCoords.y + orientationVectorY,
317517bb4c859a2bb8d30316204f39bf5b6c89c3e4dJeff Brown                            mPaint);
31890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
31990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
32090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
32190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
3228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
323fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown    private void logPointerCoords(int action, int index, MotionEvent.PointerCoords coords, int id,
324fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown            int toolType, int buttonState) {
32533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        final String prefix;
32633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        switch (action & MotionEvent.ACTION_MASK) {
32733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_DOWN:
32833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "DOWN";
32933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
33033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_UP:
33133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "UP";
33233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
33333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_MOVE:
33433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "MOVE";
33533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
33633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_CANCEL:
33733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "CANCEL";
33833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
33933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_OUTSIDE:
34033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "OUTSIDE";
34133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
34233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_DOWN:
34333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
34433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
34533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "DOWN";
34633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
34733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
34833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
34933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
35033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_UP:
35133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
35233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
35333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "UP";
35433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
35533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
35633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
35733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
35833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_HOVER_MOVE:
35933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "HOVER MOVE";
36033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
361a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_ENTER:
362a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER ENTER";
363a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
364a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_EXIT:
365a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER EXIT";
366a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
36733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_SCROLL:
36833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "SCROLL";
36933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
37033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            default:
37133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = Integer.toString(action);
37233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
37333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
37433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
3758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, mText.clear()
3768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append("Pointer ").append(id + 1)
37733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(": ")
37833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(prefix)
37933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
3808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(") Pressure=").append(coords.pressure, 3)
3818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Size=").append(coords.size, 3)
3828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMajor=").append(coords.touchMajor, 3)
3838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMinor=").append(coords.touchMinor, 3)
3848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMajor=").append(coords.toolMajor, 3)
3858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMinor=").append(coords.toolMinor, 3)
3868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
3876f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append("deg")
38880fd47ce75253dcdc2cfa85d7a3f42634b923a47Jeff Brown                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
3896f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
3906f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
391fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
392fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
3936f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .toString());
3948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
39590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
39633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public void addPointerEvent(MotionEvent event) {
39790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        synchronized (mPointers) {
398fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown            final int action = event.getAction();
39990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            int NP = mPointers.size();
400fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown
4018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_DOWN
4028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
4038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
4048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
4058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (action == MotionEvent.ACTION_DOWN) {
4068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    for (int p=0; p<NP; p++) {
4078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        final PointerState ps = mPointers.get(p);
4088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        ps.clearTrace();
4098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        ps.mCurDown = false;
4108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    }
4118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mCurDown = true;
41233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    mCurNumPointers = 0;
4138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mMaxNumPointers = 0;
4148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mVelocity.clear();
41590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
41633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
41733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                mCurNumPointers += 1;
41833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (mMaxNumPointers < mCurNumPointers) {
41933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    mMaxNumPointers = mCurNumPointers;
42033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
42133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
422b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
42390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                while (NP <= id) {
42490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    PointerState ps = new PointerState();
42590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    mPointers.add(ps);
42690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    NP++;
42790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
4288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
429d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                if (mActivePointerId < 0 ||
43033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        !mPointers.get(mActivePointerId).mCurDown) {
431d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    mActivePointerId = id;
432d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                }
433d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
43490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
43590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = true;
43690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
437cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown
43833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            final int NI = event.getPointerCount();
4399e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
4409e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown            mVelocity.addMovement(event);
4419e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown            mVelocity.computeCurrentVelocity(1);
44233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
44333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            final int N = event.getHistorySize();
44433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            for (int historyPos = 0; historyPos < N; historyPos++) {
44533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                for (int i = 0; i < NI; i++) {
44633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    final int id = event.getPointerId(i);
44733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    final PointerState ps = mCurDown ? mPointers.get(id) : null;
44833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
44933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    event.getHistoricalPointerCoords(i, historyPos, coords);
45090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    if (mPrintCoords) {
451fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                        logPointerCoords(action, i, coords, id,
452fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                                event.getToolType(i), event.getButtonState());
453cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    }
454cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    if (ps != null) {
45533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        ps.addTrace(coords.x, coords.y);
45690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    }
45790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
45833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            }
45933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            for (int i = 0; i < NI; i++) {
46033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final int id = event.getPointerId(i);
46133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final PointerState ps = mCurDown ? mPointers.get(id) : null;
46233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
463cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                event.getPointerCoords(i, coords);
46490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
465fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                    logPointerCoords(action, i, coords, id,
466fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                            event.getToolType(i), event.getButtonState());
467cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                }
468cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                if (ps != null) {
469cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    ps.addTrace(coords.x, coords.y);
470cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    ps.mXVelocity = mVelocity.getXVelocity(id);
471cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    ps.mYVelocity = mVelocity.getYVelocity(id);
47290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
47390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
47433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
4758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_UP
476d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    || action == MotionEvent.ACTION_CANCEL
4778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
4788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
4798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
4808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
481b125dc5599468a09d82751cd76152071ae485afbDianne Hackborn                final int id = event.getPointerId(index);
48290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                final PointerState ps = mPointers.get(id);
48390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                ps.mCurDown = false;
484d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown
485d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                if (action == MotionEvent.ACTION_UP
486d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                        || action == MotionEvent.ACTION_CANCEL) {
4878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    mCurDown = false;
48833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    mCurNumPointers = 0;
4898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                } else {
49033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    mCurNumPointers -= 1;
491d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    if (mActivePointerId == id) {
492d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                        mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
493d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown                    }
4948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    ps.addTrace(Float.NaN, Float.NaN);
49590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
49690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
497fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown
49890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            postInvalidate();
49990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
50090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
50190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
50290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
50390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
50433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        addPointerEvent(event);
505c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
506c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
507c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            requestFocus();
508c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
50990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
51090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
5117d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
5127d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    @Override
51333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public boolean onGenericMotionEvent(MotionEvent event) {
514c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        final int source = event.getSource();
515c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
51633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            addPointerEvent(event);
517c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
518c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Joystick: " + event);
519c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
520c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Position: " + event);
521c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else {
522c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Generic: " + event);
523c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
524c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
525c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
526c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
527c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
528c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyDown(int keyCode, KeyEvent event) {
529c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
530c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            final int repeatCount = event.getRepeatCount();
531c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            if (repeatCount == 0) {
532c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Down: " + event);
533c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            } else {
534c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
535c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            }
53633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            return true;
53733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
538c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyDown(keyCode, event);
539c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
540c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
541c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
542c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyUp(int keyCode, KeyEvent event) {
543c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
544c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Key Up: " + event);
545c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            return true;
546c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
547c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyUp(keyCode, event);
548c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
549c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
550c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    private static boolean shouldLogKey(int keyCode) {
551c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        switch (keyCode) {
552c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_UP:
553c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_DOWN:
554c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_LEFT:
555c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_RIGHT:
556c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
557c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return true;
558c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            default:
559c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return KeyEvent.isGamepadButton(keyCode)
560c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                    || KeyEvent.isModifierKey(keyCode);
561c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
56233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    }
56333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
56433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    @Override
5657d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    public boolean onTrackballEvent(MotionEvent event) {
5668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, "Trackball: " + event);
567c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
5687d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    }
5697d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
5708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // HACK
5718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // A quick and dirty string builder implementation optimized for GC.
572d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // Using String.format causes the application grind to a halt when
573d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // more than a couple of pointers are down due to the number of
574d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // temporary objects allocated while formatting strings for drawing or logging.
5758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final class FasterStringBuilder {
5768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private char[] mChars;
5778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mLength;
5788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder() {
5808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mChars = new char[64];
5818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder clear() {
5848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = 0;
5858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
5868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(String value) {
5898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int valueLength = value.length();
5908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int index = reserve(valueLength);
5918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value.getChars(0, valueLength, mChars, index);
5928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength += valueLength;
5938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
5948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
5968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value) {
5978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return append(value, 0);
5988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
5998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value, int zeroPadWidth) {
6018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final boolean negative = value < 0;
6028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
6038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = - value;
6048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (value < 0) {
6058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    append("-2147483648");
6068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    return this;
6078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
6088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int index = reserve(11);
6118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] chars = mChars;
6128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (value == 0) {
6148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '0';
6158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mLength += 1;
6168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                return this;
6178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
6208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '-';
6218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int divisor = 1000000000;
6248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int numberWidth = 10;
6258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            while (value < divisor) {
6268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
6278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                numberWidth -= 1;
6288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (numberWidth < zeroPadWidth) {
6298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    chars[index++] = '0';
6308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
6318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            do {
6348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                int digit = value / divisor;
6358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= digit * divisor;
6368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
6378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = (char) (digit + '0');
6388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            } while (divisor != 0);
6398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = index;
6418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
6428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(float value, int precision) {
6458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int scale = 1;
6468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int i = 0; i < precision; i++) {
6478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                scale *= 10;
6488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value = (float) (Math.rint(value * scale) / scale);
6508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            append((int) value);
6528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (precision != 0) {
6548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append(".");
6558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = Math.abs(value);
6568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= Math.floor(value);
6578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append((int) (value * scale), precision);
6588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
6618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        @Override
6648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public String toString() {
6658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return new String(mChars, 0, mLength);
6668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
6688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int reserve(int length) {
6698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldLength = mLength;
6708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int newLength = mLength + length;
6718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] oldChars = mChars;
6728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldCapacity = oldChars.length;
6738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (newLength > oldCapacity) {
6748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int newCapacity = oldCapacity * 2;
6758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final char[] newChars = new char[newCapacity];
6768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
6778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mChars = newChars;
6788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
6798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return oldLength;
6808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
6818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
68290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
683