PointerLocationView.java revision 76936eb96acdaf7feb221000be3e0d154fa5000c
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;
24af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brownimport android.hardware.input.InputManager;
25af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brownimport android.hardware.input.InputManager.InputDeviceListener;
269eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brownimport android.os.SystemProperties;
2790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.util.Log;
288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownimport android.view.InputDevice;
29c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brownimport android.view.KeyEvent;
3090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.MotionEvent;
3190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.VelocityTracker;
3290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.View;
3390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.view.ViewConfiguration;
34cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brownimport android.view.MotionEvent.PointerCoords;
3590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
3690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.ArrayList;
3790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
38af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brownpublic class PointerLocationView extends View implements InputDeviceListener {
398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final String TAG = "Pointer";
409eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
419eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown    // The system property key used to specify an alternate velocity tracker strategy
429eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown    // to plot alongside the default one.  Useful for testing and comparison purposes.
439eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown    private static final String ALT_STRATEGY_PROPERY_KEY = "debug.velocitytracker.alt";
449eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
4590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public static class PointerState {
468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Trace of previous points.
478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceX = new float[32];
488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private float[] mTraceY = new float[32];
4976936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        private boolean[] mTraceCurrent = new boolean[32];
508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mTraceCount;
518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // True if the pointer is down.
5390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private boolean mCurDown;
548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent coordinates.
56cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown        private PointerCoords mCoords = new PointerCoords();
5765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        private int mToolType;
588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent velocity.
609e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mXVelocity;
619e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mYVelocity;
629eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private float mAltXVelocity;
639eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private float mAltYVelocity;
64b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
6586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        // Current bounding box, if any
6686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private boolean mHasBoundingBox;
6786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingLeft;
6886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingTop;
6986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingRight;
7086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingBottom;
7186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
72b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        // Position estimator.
73b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
749eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private VelocityTracker.Estimator mAltEstimator = new VelocityTracker.Estimator();
75b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void clearTrace() {
778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount = 0;
788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8076936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        public void addTrace(float x, float y, boolean current) {
818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int traceCapacity = mTraceX.length;
828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (mTraceCount == traceCapacity) {
838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                traceCapacity *= 2;
848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceX = new float[traceCapacity];
858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceX = newTraceX;
878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceY = new float[traceCapacity];
898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceY = newTraceY;
9176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright
9276936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                boolean[] newTraceCurrent = new boolean[traceCapacity];
9376936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
9476936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                mTraceCurrent= newTraceCurrent;
958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceX[mTraceCount] = x;
988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceY[mTraceCount] = y;
9976936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright            mTraceCurrent[mTraceCount] = current;
1008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount += 1;
1018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
10290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
10390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
104b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_PAST_POINTS = 4;
105b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_FUTURE_POINTS = 2;
106b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final float ESTIMATE_INTERVAL = 0.02f;
107b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
108af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private final InputManager mIm;
109af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
11090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
11576936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright    private final Paint mCurrentPointPaint;
11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
11790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
123d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    private int mActivePointerId;
1248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
12565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private final PointerCoords mTempCoords = new PointerCoords();
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1279e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown    private final VelocityTracker mVelocity;
1289eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown    private final VelocityTracker mAltVelocity;
1299eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
1308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final FasterStringBuilder mText = new FasterStringBuilder();
1318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
13290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
13390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
13490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
13590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
136c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        setFocusableInTouchMode(true);
137c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
138af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm = (InputManager)c.getSystemService(Context.INPUT_SERVICE);
139af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
14090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
14190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
14290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
14390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
14490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
14590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
15776936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        mCurrentPointPaint = new Paint();
15876936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        mCurrentPointPaint.setAntiAlias(true);
15976936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        mCurrentPointPaint.setARGB(255, 255, 0, 0);
16076936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        mCurrentPointPaint.setStyle(Paint.Style.STROKE);
16176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright        mCurrentPointPaint.setStrokeWidth(2);
16290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
16390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
16490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
16590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
16690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
16790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
16890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
16990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
17090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
17190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
17290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
173d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown        mActivePointerId = 0;
1749e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1759e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        mVelocity = VelocityTracker.obtain();
1769eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
1779eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
1789eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        if (altStrategy.length() != 0) {
1799eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            Log.d(TAG, "Comparing default velocity tracker strategy with " + altStrategy);
1809eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity = VelocityTracker.obtain(altStrategy);
1819eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        } else {
1829eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity = null;
1839eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        }
18490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
18590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
18790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
19190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
19290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
19390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
19490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
19590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
19690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
19790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
19890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
19990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
20090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
20190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
20290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
2038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
2048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
2058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // angles less than or greater than 0 radians rotate the major axis left or right.
2068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private RectF mReusableOvalRect = new RectF();
2078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
2088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            float angle, Paint paint) {
2098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.save(Canvas.MATRIX_SAVE_FLAG);
2105068ad8d2396d6d7bfbdb1c2c3fe57104744f1f9Jeff Brown        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
2118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.left = x - minor / 2;
2128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.right = x + minor / 2;
2138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.top = y - major / 2;
2148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.bottom = y + major / 2;
2158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.drawOval(mReusableOvalRect, paint);
2168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.restore();
2178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
21890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
21990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
22090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
22170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int w = getWidth();
22270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int itemW = w/7;
22370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int base = -mTextMetrics.ascent+1;
22470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int bottom = mHeaderBottom;
22570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
22670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NP = mPointers.size();
22770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
22870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Labels
22970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (mActivePointerId >= 0) {
23070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(mActivePointerId);
23190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
23270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
23370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
23470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("P: ").append(mCurNumPointers)
23570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append(" / ").append(mMaxNumPointers)
23670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1, base, mTextPaint);
23770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
23870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
23970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if ((mCurDown && ps.mCurDown) || N == 0) {
24070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
2418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
24270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("X: ").append(ps.mCoords.x, 1)
24370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
24470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
2458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
24670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("Y: ").append(ps.mCoords.y, 1)
24770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
24870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
24970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
25070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
25170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
25270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dx) < mVC.getScaledTouchSlop()
25370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
25570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dX: ").append(dx, 1)
25670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
25770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
25870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dy) < mVC.getScaledTouchSlop()
25970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
26170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dY: ").append(dy, 1)
26270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
26390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
26470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
26570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
26670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
26770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Xv: ").append(ps.mXVelocity, 3)
26870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 3, base, mTextPaint);
26970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
27070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
27170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
27270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Yv: ").append(ps.mYVelocity, 3)
27370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 4, base, mTextPaint);
27470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
27570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
27670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
27770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
27870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
27970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Prs: ").append(ps.mCoords.pressure, 2)
28070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 5, base, mTextPaint);
28170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
28270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
28370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
28470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
28570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
28670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Size: ").append(ps.mCoords.size, 2)
28770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 6, base, mTextPaint);
28870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
28970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
29070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Pointer trace.
29170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int p = 0; p < NP; p++) {
29270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(p);
29370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
29470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            // Draw path.
29570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
29670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            float lastX = 0, lastY = 0;
29770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean haveLast = false;
29870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean drawn = false;
29970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mPaint.setARGB(255, 128, 255, 255);
30070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            for (int i=0; i < N; i++) {
30170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float x = ps.mTraceX[i];
30270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float y = ps.mTraceY[i];
30370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (Float.isNaN(x)) {
30470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    haveLast = false;
30570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    continue;
30690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
30770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (haveLast) {
30870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
30976936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                    final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
31076936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                    canvas.drawPoint(lastX, lastY, paint);
31170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    drawn = true;
3128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
31370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastX = x;
31470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastY = y;
31570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                haveLast = true;
31670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
31770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
31870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (drawn) {
31970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw movement estimate curve.
32070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(128, 128, 0, 128);
32170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
32270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
32370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
32470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
32570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
32670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lx, ly, x, y, mPaint);
32770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    lx = x;
32870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ly = y;
32970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
33070825161b5bf51ed48319e142751a9c88b104994Jeff Brown
33170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw velocity vector.
33270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, 255, 64, 128);
33370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float xVel = ps.mXVelocity * (1000 / 60);
33470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float yVel = ps.mYVelocity * (1000 / 60);
33570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
3369eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
3379eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                // Draw alternate estimate.
3389eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
3399eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mPaint.setARGB(128, 0, 128, 128);
3409eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
3419eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
3429eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
3439eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL);
3449eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL);
3459eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        canvas.drawLine(lx, ly, x, y, mPaint);
3469eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        lx = x;
3479eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        ly = y;
3489eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    }
3499eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
3509eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mPaint.setARGB(255, 64, 255, 128);
3519eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    xVel = ps.mAltXVelocity * (1000 / 60);
3529eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    yVel = ps.mAltYVelocity * (1000 / 60);
3539eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
3549eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
35570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
35670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
35770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mCurDown && ps.mCurDown) {
35870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw crosshairs.
35970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
36070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
36170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
36270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current point.
36370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                int pressureLevel = (int)(ps.mCoords.pressure * 255);
36470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
36570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
36670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
36770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current touch ellipse.
36870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
36970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
37070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
37170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
37270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current tool ellipse.
37370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
37470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
37570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
37670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
37770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the orientation arrow.
37870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float arrowSize = ps.mCoords.toolMajor * 0.7f;
37970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (arrowSize < 20) {
38070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    arrowSize = 20;
38170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
38270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 0);
38370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
38470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
38570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
38670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
38770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
38870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
38970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show full circle orientation.
39070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
39170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
39270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
39370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
39470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                } else {
39570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show half circle orientation.
39670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(
39770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x - orientationVectorX,
39870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y - orientationVectorY,
39970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
40070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
40170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
40290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
40370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
40470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the tilt point along the orientation arrow.
40570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float tiltScale = (float) Math.sin(
40670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
40770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawCircle(
40870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.x + orientationVectorX * tiltScale,
40970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.y + orientationVectorY * tiltScale,
41070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        3.0f, mPaint);
41186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
41286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                // Draw the current bounding box
41386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                if (ps.mHasBoundingBox) {
41486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    canvas.drawRect(ps.mBoundingLeft, ps.mBoundingTop,
41586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                            ps.mBoundingRight, ps.mBoundingBottom, mPaint);
41686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                }
41790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
41890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
41990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
42065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
42165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logMotionEvent(String type, MotionEvent event) {
42265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int action = event.getAction();
42365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int N = event.getHistorySize();
42465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int NI = event.getPointerCount();
42565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
42665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            for (int i = 0; i < NI; i++) {
42765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final int id = event.getPointerId(i);
42865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                event.getHistoricalPointerCoords(i, historyPos, mTempCoords);
42986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                logCoords(type, action, i, mTempCoords, id, event);
43065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            }
43165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
43265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int i = 0; i < NI; i++) {
43365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            final int id = event.getPointerId(i);
43465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            event.getPointerCoords(i, mTempCoords);
43586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright            logCoords(type, action, i, mTempCoords, id, event);
43665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
43765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    }
43865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
43965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logCoords(String type, int action, int index,
44086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright            MotionEvent.PointerCoords coords, int id, MotionEvent event) {
44186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        final int toolType = event.getToolType(index);
44286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        final int buttonState = event.getButtonState();
44333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        final String prefix;
44433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        switch (action & MotionEvent.ACTION_MASK) {
44533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_DOWN:
44633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "DOWN";
44733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
44833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_UP:
44933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "UP";
45033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
45133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_MOVE:
45233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "MOVE";
45333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
45433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_CANCEL:
45533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "CANCEL";
45633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
45733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_OUTSIDE:
45833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "OUTSIDE";
45933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
46033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_DOWN:
46133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
46233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
46333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "DOWN";
46433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
46533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
46633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
46733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
46833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_UP:
46933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
47033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
47133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "UP";
47233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
47333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
47433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
47533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
47633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_HOVER_MOVE:
47733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "HOVER MOVE";
47833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
479a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_ENTER:
480a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER ENTER";
481a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
482a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_EXIT:
483a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER EXIT";
484a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
48533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_SCROLL:
48633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "SCROLL";
48733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
48833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            default:
48933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = Integer.toString(action);
49033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
49133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
49233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
4938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, mText.clear()
49465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(type).append(" id ").append(id + 1)
49533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(": ")
49633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(prefix)
49733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
4988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(") Pressure=").append(coords.pressure, 3)
4998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Size=").append(coords.size, 3)
5008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMajor=").append(coords.touchMajor, 3)
5018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMinor=").append(coords.touchMinor, 3)
5028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMajor=").append(coords.toolMajor, 3)
5038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMinor=").append(coords.toolMinor, 3)
5048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
5056f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append("deg")
50665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(" Tilt=").append((float)(
50765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                        coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
50865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append("deg")
50980fd47ce75253dcdc2cfa85d7a3f42634b923a47Jeff Brown                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
5106f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
5116f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
51286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(" BoundingBox=[(")
51386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(event.getAxisValue(MotionEvent.AXIS_GENERIC_1), 3)
51486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_2), 3).append(")")
51586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", (").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_3), 3)
51686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_4), 3)
51786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(")]")
518fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
519fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
5206f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .toString());
5218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
52290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
52333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public void addPointerEvent(MotionEvent event) {
52470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int action = event.getAction();
52570825161b5bf51ed48319e142751a9c88b104994Jeff Brown        int NP = mPointers.size();
52633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
52770825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_DOWN
52870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
52970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
53070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
53170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (action == MotionEvent.ACTION_DOWN) {
53270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int p=0; p<NP; p++) {
53370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    final PointerState ps = mPointers.get(p);
53470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.clearTrace();
53570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.mCurDown = false;
53633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
53770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = true;
53870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
53970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = 0;
54070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mVelocity.clear();
5419eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
5429eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mAltVelocity.clear();
5439eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
54470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
54533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
54670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mCurNumPointers += 1;
54770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mMaxNumPointers < mCurNumPointers) {
54870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = mCurNumPointers;
54990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
550cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown
55170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
55270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            while (NP <= id) {
55370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                PointerState ps = new PointerState();
55470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPointers.add(ps);
55570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                NP++;
55670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
55770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
55870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mActivePointerId < 0 ||
55970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    !mPointers.get(mActivePointerId).mCurDown) {
56070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mActivePointerId = id;
56133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            }
56270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
56370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
56470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = true;
5652f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright            InputDevice device = InputDevice.getDevice(event.getDeviceId());
5662f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright            ps.mHasBoundingBox = device != null &&
5672f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright                    device.getMotionRange(MotionEvent.AXIS_GENERIC_1) != null;
56870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
56970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
57070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NI = event.getPointerCount();
57170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
57270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.addMovement(event);
57370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.computeCurrentVelocity(1);
5749eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        if (mAltVelocity != null) {
5759eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity.addMovement(event);
5769eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity.computeCurrentVelocity(1);
5779eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        }
57870825161b5bf51ed48319e142751a9c88b104994Jeff Brown
57970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int N = event.getHistorySize();
58070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
58133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            for (int i = 0; i < NI; i++) {
58233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final int id = event.getPointerId(i);
58333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final PointerState ps = mCurDown ? mPointers.get(id) : null;
58465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
58570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                event.getHistoricalPointerCoords(i, historyPos, coords);
58690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
58786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    logCoords("Pointer", action, i, coords, id, event);
588cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                }
589cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                if (ps != null) {
59076936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                    ps.addTrace(coords.x, coords.y, false);
59190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
59290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
59370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
59470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int i = 0; i < NI; i++) {
59570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(i);
59670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mCurDown ? mPointers.get(id) : null;
59770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
59870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            event.getPointerCoords(i, coords);
59970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mPrintCoords) {
60086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                logCoords("Pointer", action, i, coords, id, event);
60170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
60270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (ps != null) {
60376936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                ps.addTrace(coords.x, coords.y, true);
60470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mXVelocity = mVelocity.getXVelocity(id);
60570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mYVelocity = mVelocity.getYVelocity(id);
60685bd0d62830a098c1bdc720dfdcf4fe1b18b657cJeff Brown                mVelocity.getEstimator(id, ps.mEstimator);
6079eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
6089eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
6099eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
6109eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mAltVelocity.getEstimator(id, ps.mAltEstimator);
6119eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
61270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mToolType = event.getToolType(i);
61386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
61486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                if (ps.mHasBoundingBox) {
61586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingLeft = event.getAxisValue(MotionEvent.AXIS_GENERIC_1, i);
61686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingTop = event.getAxisValue(MotionEvent.AXIS_GENERIC_2, i);
61786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingRight = event.getAxisValue(MotionEvent.AXIS_GENERIC_3, i);
61886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingBottom = event.getAxisValue(MotionEvent.AXIS_GENERIC_4, i);
61986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                }
62070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
62170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
62270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
62370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_UP
62470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || action == MotionEvent.ACTION_CANCEL
62570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
62670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
62770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
62870825161b5bf51ed48319e142751a9c88b104994Jeff Brown
62970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
63070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
63170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = false;
63233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
6338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_UP
63470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    || action == MotionEvent.ACTION_CANCEL) {
63570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = false;
63670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
63770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
63870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers -= 1;
63970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (mActivePointerId == id) {
64070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
64190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
64276936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright                ps.addTrace(Float.NaN, Float.NaN, false);
64390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
64490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
64570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
64670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        invalidate();
64790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
64890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
64990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
65090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
65133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        addPointerEvent(event);
652c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
653c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
654c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            requestFocus();
655c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
65690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
65790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
6587d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
6597d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    @Override
66033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public boolean onGenericMotionEvent(MotionEvent event) {
661c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        final int source = event.getSource();
662c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
66333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            addPointerEvent(event);
664c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
66565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Joystick", event);
666c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
66765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Position", event);
668c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else {
66965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Generic", event);
670c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
671c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
672c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
673c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
674c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
675c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyDown(int keyCode, KeyEvent event) {
676c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
677c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            final int repeatCount = event.getRepeatCount();
678c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            if (repeatCount == 0) {
679c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Down: " + event);
680c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            } else {
681c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
682c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            }
68333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            return true;
68433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
685c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyDown(keyCode, event);
686c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
687c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
688c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
689c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyUp(int keyCode, KeyEvent event) {
690c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
691c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Key Up: " + event);
692c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            return true;
693c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
694c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyUp(keyCode, event);
695c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
696c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
697c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    private static boolean shouldLogKey(int keyCode) {
698c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        switch (keyCode) {
699c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_UP:
700c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_DOWN:
701c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_LEFT:
702c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_RIGHT:
703c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
704c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return true;
705c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            default:
706c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return KeyEvent.isGamepadButton(keyCode)
707c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                    || KeyEvent.isModifierKey(keyCode);
708c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
70933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    }
71033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
71133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    @Override
7127d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    public boolean onTrackballEvent(MotionEvent event) {
71365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        logMotionEvent("Trackball", event);
714c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
7157d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    }
716af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
717af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
718af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    protected void onAttachedToWindow() {
719af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        super.onAttachedToWindow();
720af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
721af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm.registerInputDeviceListener(this, getHandler());
722af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDevices();
723af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
724af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
725af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
726af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    protected void onDetachedFromWindow() {
727af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        super.onDetachedFromWindow();
728af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
729af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm.unregisterInputDeviceListener(this);
730af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
731af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
732af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
733af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceAdded(int deviceId) {
734af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Added");
735af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
736af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
737af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
738af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceChanged(int deviceId) {
739af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Changed");
740af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
741af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
742af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
743af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceRemoved(int deviceId) {
744af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Removed");
745af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
746af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
747af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private void logInputDevices() {
748af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        int[] deviceIds = InputDevice.getDeviceIds();
749af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        for (int i = 0; i < deviceIds.length; i++) {
750af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            logInputDeviceState(deviceIds[i], "Device Enumerated");
751af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        }
752af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
753af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
754af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private void logInputDeviceState(int deviceId, String state) {
755af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        InputDevice device = mIm.getInputDevice(deviceId);
756af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        if (device != null) {
757af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            Log.i(TAG, state + ": " + device);
758af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        } else {
759af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            Log.i(TAG, state + ": " + deviceId);
760af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        }
761af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
762af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
7638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // HACK
7648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // A quick and dirty string builder implementation optimized for GC.
765d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // Using String.format causes the application grind to a halt when
766d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // more than a couple of pointers are down due to the number of
767d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // temporary objects allocated while formatting strings for drawing or logging.
7688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final class FasterStringBuilder {
7698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private char[] mChars;
7708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mLength;
7718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder() {
7738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mChars = new char[64];
7748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder clear() {
7778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = 0;
7788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(String value) {
7828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int valueLength = value.length();
7838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int index = reserve(valueLength);
7848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value.getChars(0, valueLength, mChars, index);
7858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength += valueLength;
7868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value) {
7908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return append(value, 0);
7918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value, int zeroPadWidth) {
7948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final boolean negative = value < 0;
7958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
7968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = - value;
7978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (value < 0) {
7988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    append("-2147483648");
7998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    return this;
8008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
8018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int index = reserve(11);
8048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] chars = mChars;
8058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (value == 0) {
8078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '0';
8088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mLength += 1;
8098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                return this;
8108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
8138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '-';
8148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int divisor = 1000000000;
8178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int numberWidth = 10;
8188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            while (value < divisor) {
8198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
8208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                numberWidth -= 1;
8218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (numberWidth < zeroPadWidth) {
8228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    chars[index++] = '0';
8238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
8248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            do {
8278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                int digit = value / divisor;
8288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= digit * divisor;
8298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
8308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = (char) (digit + '0');
8318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            } while (divisor != 0);
8328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = index;
8348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
8358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(float value, int precision) {
8388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int scale = 1;
8398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int i = 0; i < precision; i++) {
8408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                scale *= 10;
8418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value = (float) (Math.rint(value * scale) / scale);
8438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            append((int) value);
8458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (precision != 0) {
8478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append(".");
8488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = Math.abs(value);
8498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= Math.floor(value);
8508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append((int) (value * scale), precision);
8518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
8548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        @Override
8578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public String toString() {
8588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return new String(mChars, 0, mLength);
8598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int reserve(int length) {
8628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldLength = mLength;
8638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int newLength = mLength + length;
8648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] oldChars = mChars;
8658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldCapacity = oldChars.length;
8668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (newLength > oldCapacity) {
8678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int newCapacity = oldCapacity * 2;
8688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final char[] newChars = new char[newCapacity];
8698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
8708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mChars = newChars;
8718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return oldLength;
8738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
87590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
876