PointerLocationView.java revision 2f1cd7e68bc1f45243191f63984523ba6d36b590
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];
498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mTraceCount;
508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // True if the pointer is down.
5290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        private boolean mCurDown;
538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent coordinates.
55cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown        private PointerCoords mCoords = new PointerCoords();
5665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        private int mToolType;
578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        // Most recent velocity.
599e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mXVelocity;
609e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        private float mYVelocity;
619eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private float mAltXVelocity;
629eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private float mAltYVelocity;
63b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
6486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        // Current bounding box, if any
6586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private boolean mHasBoundingBox;
6686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingLeft;
6786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingTop;
6886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingRight;
6986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        private float mBoundingBottom;
7086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
71b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        // Position estimator.
72b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown        private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
739eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        private VelocityTracker.Estimator mAltEstimator = new VelocityTracker.Estimator();
74b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void clearTrace() {
768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount = 0;
778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public void addTrace(float x, float y) {
808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int traceCapacity = mTraceX.length;
818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (mTraceCount == traceCapacity) {
828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                traceCapacity *= 2;
838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceX = new float[traceCapacity];
848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceX = newTraceX;
868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                float[] newTraceY = new float[traceCapacity];
888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mTraceY = newTraceY;
908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceX[mTraceCount] = x;
938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceY[mTraceCount] = y;
948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mTraceCount += 1;
958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
9690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
9790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
98b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_PAST_POINTS = 4;
99b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final int ESTIMATE_FUTURE_POINTS = 2;
100b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown    private final float ESTIMATE_INTERVAL = 0.02f;
101b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown
102af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private final InputManager mIm;
103af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
10490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final ViewConfiguration mVC;
10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextPaint;
10690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextBackgroundPaint;
10790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTextLevelPaint;
10890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPaint;
10990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mTargetPaint;
11090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final Paint mPathPaint;
11190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mHeaderBottom;
11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mCurDown;
11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mCurNumPointers;
11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private int mMaxNumPointers;
116d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    private int mActivePointerId;
1178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
11865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private final PointerCoords mTempCoords = new PointerCoords();
11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
1209e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown    private final VelocityTracker mVelocity;
1219eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown    private final VelocityTracker mAltVelocity;
1229eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
1238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private final FasterStringBuilder mText = new FasterStringBuilder();
1248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
12590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    private boolean mPrintCoords = true;
12690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
12790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public PointerLocationView(Context c) {
12890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super(c);
129c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        setFocusableInTouchMode(true);
130c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
131af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm = (InputManager)c.getSystemService(Context.INPUT_SERVICE);
132af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
13390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mVC = ViewConfiguration.get(c);
13490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint = new Paint();
13590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setAntiAlias(true);
13690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setTextSize(10
13790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                * getResources().getDisplayMetrics().density);
13890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.setARGB(255, 0, 0, 0);
13990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint = new Paint();
14090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setAntiAlias(false);
14190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
14290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint = new Paint();
14390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setAntiAlias(false);
14490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextLevelPaint.setARGB(192, 255, 0, 0);
14590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint = new Paint();
14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setAntiAlias(true);
14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setARGB(255, 255, 255, 255);
14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(2);
15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint = new Paint();
15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setAntiAlias(false);
15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTargetPaint.setARGB(255, 0, 0, 192);
15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint = new Paint();
15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setAntiAlias(false);
15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPathPaint.setARGB(255, 0, 96, 255);
15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStyle(Paint.Style.STROKE);
15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPaint.setStrokeWidth(1);
15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
15990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        PointerState ps = new PointerState();
16090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPointers.add(ps);
161d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown        mActivePointerId = 0;
1629e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown
1639e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown        mVelocity = VelocityTracker.obtain();
1649eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
1659eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
1669eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        if (altStrategy.length() != 0) {
1679eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            Log.d(TAG, "Comparing default velocity tracker strategy with " + altStrategy);
1689eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity = VelocityTracker.obtain(altStrategy);
1699eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        } else {
1709eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity = null;
1719eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        }
17290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
17390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
17490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public void setPrintCoords(boolean state) {
17590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mPrintCoords = state;
17690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
17790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
17890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
17990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
18090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
18190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mTextPaint.getFontMetricsInt(mTextMetrics);
18290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
18390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        if (false) {
18490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
18590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " descent=" + mTextMetrics.descent
18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " leading=" + mTextMetrics.leading
18790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " top=" + mTextMetrics.top
18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                    + " bottom=" + mTextMetrics.bottom);
18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
1918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
1928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
1938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // angles less than or greater than 0 radians rotate the major axis left or right.
1948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private RectF mReusableOvalRect = new RectF();
1958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
1968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            float angle, Paint paint) {
1978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.save(Canvas.MATRIX_SAVE_FLAG);
1985068ad8d2396d6d7bfbdb1c2c3fe57104744f1f9Jeff Brown        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
1998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.left = x - minor / 2;
2008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.right = x + minor / 2;
2018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.top = y - major / 2;
2028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        mReusableOvalRect.bottom = y + major / 2;
2038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.drawOval(mReusableOvalRect, paint);
2048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        canvas.restore();
2058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
20690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
20790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
20890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    protected void onDraw(Canvas canvas) {
20970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int w = getWidth();
21070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int itemW = w/7;
21170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int base = -mTextMetrics.ascent+1;
21270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int bottom = mHeaderBottom;
21370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
21470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NP = mPointers.size();
21570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
21670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Labels
21770825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (mActivePointerId >= 0) {
21870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(mActivePointerId);
21990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
22070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
22170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
22270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("P: ").append(mCurNumPointers)
22370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append(" / ").append(mMaxNumPointers)
22470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1, base, mTextPaint);
22570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
22670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
22770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if ((mCurDown && ps.mCurDown) || N == 0) {
22870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
2298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
23070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("X: ").append(ps.mCoords.x, 1)
23170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
23270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
2338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
23470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("Y: ").append(ps.mCoords.y, 1)
23570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
23670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
23770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
23870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
23970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
24070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dx) < mVC.getScaledTouchSlop()
24170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
24370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dX: ").append(dx, 1)
24470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW, base, mTextPaint);
24570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
24670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        Math.abs(dy) < mVC.getScaledTouchSlop()
24770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ? mTextBackgroundPaint : mTextLevelPaint);
2488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                canvas.drawText(mText.clear()
24970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .append("dY: ").append(dy, 1)
25070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        .toString(), 1 + itemW * 2, base, mTextPaint);
25190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
25270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
25370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
25470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
25570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Xv: ").append(ps.mXVelocity, 3)
25670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 3, base, mTextPaint);
25770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
25870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
25970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
26070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Yv: ").append(ps.mYVelocity, 3)
26170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 4, base, mTextPaint);
26270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
26370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
26470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
26570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
26670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
26770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Prs: ").append(ps.mCoords.pressure, 2)
26870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 5, base, mTextPaint);
26970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
27070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
27170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
27270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    bottom, mTextLevelPaint);
27370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            canvas.drawText(mText.clear()
27470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .append("Size: ").append(ps.mCoords.size, 2)
27570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    .toString(), 1 + itemW * 6, base, mTextPaint);
27670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
27770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
27870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        // Pointer trace.
27970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int p = 0; p < NP; p++) {
28070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(p);
28170825161b5bf51ed48319e142751a9c88b104994Jeff Brown
28270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            // Draw path.
28370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int N = ps.mTraceCount;
28470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            float lastX = 0, lastY = 0;
28570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean haveLast = false;
28670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            boolean drawn = false;
28770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mPaint.setARGB(255, 128, 255, 255);
28870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            for (int i=0; i < N; i++) {
28970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float x = ps.mTraceX[i];
29070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float y = ps.mTraceY[i];
29170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (Float.isNaN(x)) {
29270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    haveLast = false;
29370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    continue;
29490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
29570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (haveLast) {
29670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
29770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawPoint(lastX, lastY, mPaint);
29870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    drawn = true;
2998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
30070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastX = x;
30170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                lastY = y;
30270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                haveLast = true;
30370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
30470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
30570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (drawn) {
30670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw movement estimate curve.
30770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(128, 128, 0, 128);
30870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
30970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
31070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
31170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
31270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
31370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(lx, ly, x, y, mPaint);
31470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    lx = x;
31570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ly = y;
31670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
31770825161b5bf51ed48319e142751a9c88b104994Jeff Brown
31870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw velocity vector.
31970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, 255, 64, 128);
32070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float xVel = ps.mXVelocity * (1000 / 60);
32170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float yVel = ps.mYVelocity * (1000 / 60);
32270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
3239eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
3249eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                // Draw alternate estimate.
3259eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
3269eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mPaint.setARGB(128, 0, 128, 128);
3279eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
3289eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
3299eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
3309eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL);
3319eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL);
3329eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        canvas.drawLine(lx, ly, x, y, mPaint);
3339eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        lx = x;
3349eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                        ly = y;
3359eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    }
3369eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown
3379eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mPaint.setARGB(255, 64, 255, 128);
3389eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    xVel = ps.mAltXVelocity * (1000 / 60);
3399eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    yVel = ps.mAltYVelocity * (1000 / 60);
3409eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
3419eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
34270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
34370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
34470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mCurDown && ps.mCurDown) {
34570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw crosshairs.
34670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
34770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
34870825161b5bf51ed48319e142751a9c88b104994Jeff Brown
34970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current point.
35070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                int pressureLevel = (int)(ps.mCoords.pressure * 255);
35170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
35270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
35370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
35470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current touch ellipse.
35570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
35670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
35770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
35870825161b5bf51ed48319e142751a9c88b104994Jeff Brown
35970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw current tool ellipse.
36070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
36170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
36270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
36370825161b5bf51ed48319e142751a9c88b104994Jeff Brown
36470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the orientation arrow.
36570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float arrowSize = ps.mCoords.toolMajor * 0.7f;
36670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (arrowSize < 20) {
36770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    arrowSize = 20;
36870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                }
36970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPaint.setARGB(255, pressureLevel, 255, 0);
37070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
37170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
37270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
37370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        * arrowSize);
37470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
37570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
37670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show full circle orientation.
37770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
37870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
37970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
38070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
38170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                } else {
38270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    // Show half circle orientation.
38370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    canvas.drawLine(
38470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x - orientationVectorX,
38570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y - orientationVectorY,
38670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.x + orientationVectorX,
38770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            ps.mCoords.y + orientationVectorY,
38870825161b5bf51ed48319e142751a9c88b104994Jeff Brown                            mPaint);
38990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
39070825161b5bf51ed48319e142751a9c88b104994Jeff Brown
39170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                // Draw the tilt point along the orientation arrow.
39270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                float tiltScale = (float) Math.sin(
39370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
39470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                canvas.drawCircle(
39570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.x + orientationVectorX * tiltScale,
39670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        ps.mCoords.y + orientationVectorY * tiltScale,
39770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                        3.0f, mPaint);
39886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
39986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                // Draw the current bounding box
40086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                if (ps.mHasBoundingBox) {
40186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    canvas.drawRect(ps.mBoundingLeft, ps.mBoundingTop,
40286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                            ps.mBoundingRight, ps.mBoundingBottom, mPaint);
40386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                }
40490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
40590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
40690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
40765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
40865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logMotionEvent(String type, MotionEvent event) {
40965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int action = event.getAction();
41065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int N = event.getHistorySize();
41165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        final int NI = event.getPointerCount();
41265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
41365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            for (int i = 0; i < NI; i++) {
41465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final int id = event.getPointerId(i);
41565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                event.getHistoricalPointerCoords(i, historyPos, mTempCoords);
41686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                logCoords(type, action, i, mTempCoords, id, event);
41765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            }
41865fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
41965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        for (int i = 0; i < NI; i++) {
42065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            final int id = event.getPointerId(i);
42165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            event.getPointerCoords(i, mTempCoords);
42286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright            logCoords(type, action, i, mTempCoords, id, event);
42365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        }
42465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    }
42565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown
42665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown    private void logCoords(String type, int action, int index,
42786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright            MotionEvent.PointerCoords coords, int id, MotionEvent event) {
42886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        final int toolType = event.getToolType(index);
42986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright        final int buttonState = event.getButtonState();
43033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        final String prefix;
43133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        switch (action & MotionEvent.ACTION_MASK) {
43233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_DOWN:
43333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "DOWN";
43433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
43533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_UP:
43633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "UP";
43733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
43833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_MOVE:
43933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "MOVE";
44033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
44133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_CANCEL:
44233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "CANCEL";
44333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
44433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_OUTSIDE:
44533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "OUTSIDE";
44633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
44733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_DOWN:
44833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
44933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
45033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "DOWN";
45133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
45233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
45333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
45433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
45533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_POINTER_UP:
45633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
45733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
45833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "UP";
45933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                } else {
46033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                    prefix = "MOVE";
46133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
46233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
46333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_HOVER_MOVE:
46433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "HOVER MOVE";
46533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
466a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_ENTER:
467a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER ENTER";
468a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
469a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown            case MotionEvent.ACTION_HOVER_EXIT:
470a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                prefix = "HOVER EXIT";
471a032cc008618b83ecbbede537517d1e7998e3264Jeff Brown                break;
47233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            case MotionEvent.ACTION_SCROLL:
47333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = "SCROLL";
47433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
47533bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            default:
47633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                prefix = Integer.toString(action);
47733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                break;
47833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
47933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
4808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        Log.i(TAG, mText.clear()
48165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(type).append(" id ").append(id + 1)
48233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(": ")
48333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(prefix)
48433bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
4858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(") Pressure=").append(coords.pressure, 3)
4868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Size=").append(coords.size, 3)
4878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMajor=").append(coords.touchMajor, 3)
4888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" TouchMinor=").append(coords.touchMinor, 3)
4898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMajor=").append(coords.toolMajor, 3)
4908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" ToolMinor=").append(coords.toolMinor, 3)
4918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
4926f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append("deg")
49365fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append(" Tilt=").append((float)(
49465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                        coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
49565fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                .append("deg")
49680fd47ce75253dcdc2cfa85d7a3f42634b923a47Jeff Brown                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
4976f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
4986f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
49986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(" BoundingBox=[(")
50086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(event.getAxisValue(MotionEvent.AXIS_GENERIC_1), 3)
50186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_2), 3).append(")")
50286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", (").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_3), 3)
50386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_4), 3)
50486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                .append(")]")
505fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
506fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown                .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
5076f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                .toString());
5088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
50990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
51033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public void addPointerEvent(MotionEvent event) {
51170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int action = event.getAction();
51270825161b5bf51ed48319e142751a9c88b104994Jeff Brown        int NP = mPointers.size();
51333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
51470825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_DOWN
51570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
51670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
51770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
51870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (action == MotionEvent.ACTION_DOWN) {
51970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                for (int p=0; p<NP; p++) {
52070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    final PointerState ps = mPointers.get(p);
52170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.clearTrace();
52270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    ps.mCurDown = false;
52333bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                }
52470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = true;
52570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
52670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = 0;
52770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mVelocity.clear();
5289eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
5299eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mAltVelocity.clear();
5309eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
53170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
53233bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
53370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            mCurNumPointers += 1;
53470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mMaxNumPointers < mCurNumPointers) {
53570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mMaxNumPointers = mCurNumPointers;
53690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
537cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown
53870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
53970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            while (NP <= id) {
54070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                PointerState ps = new PointerState();
54170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mPointers.add(ps);
54270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                NP++;
54370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
54470825161b5bf51ed48319e142751a9c88b104994Jeff Brown
54570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mActivePointerId < 0 ||
54670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    !mPointers.get(mActivePointerId).mCurDown) {
54770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mActivePointerId = id;
54833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            }
54970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
55070825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
55170825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = true;
5522f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright            InputDevice device = InputDevice.getDevice(event.getDeviceId());
5532f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright            ps.mHasBoundingBox = device != null &&
5542f1cd7e68bc1f45243191f63984523ba6d36b590Michael Wright                    device.getMotionRange(MotionEvent.AXIS_GENERIC_1) != null;
55570825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
55670825161b5bf51ed48319e142751a9c88b104994Jeff Brown
55770825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int NI = event.getPointerCount();
55870825161b5bf51ed48319e142751a9c88b104994Jeff Brown
55970825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.addMovement(event);
56070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        mVelocity.computeCurrentVelocity(1);
5619eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        if (mAltVelocity != null) {
5629eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity.addMovement(event);
5639eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown            mAltVelocity.computeCurrentVelocity(1);
5649eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown        }
56570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
56670825161b5bf51ed48319e142751a9c88b104994Jeff Brown        final int N = event.getHistorySize();
56770825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int historyPos = 0; historyPos < N; historyPos++) {
56833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            for (int i = 0; i < NI; i++) {
56933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final int id = event.getPointerId(i);
57033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown                final PointerState ps = mCurDown ? mPointers.get(id) : null;
57165fd251c3913fc921468a3dad190810db19eb9dfJeff Brown                final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
57270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                event.getHistoricalPointerCoords(i, historyPos, coords);
57390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                if (mPrintCoords) {
57486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    logCoords("Pointer", action, i, coords, id, event);
575cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                }
576cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                if (ps != null) {
577cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown                    ps.addTrace(coords.x, coords.y);
57890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
57990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
58070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
58170825161b5bf51ed48319e142751a9c88b104994Jeff Brown        for (int i = 0; i < NI; i++) {
58270825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(i);
58370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mCurDown ? mPointers.get(id) : null;
58470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
58570825161b5bf51ed48319e142751a9c88b104994Jeff Brown            event.getPointerCoords(i, coords);
58670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (mPrintCoords) {
58786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                logCoords("Pointer", action, i, coords, id, event);
58870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
58970825161b5bf51ed48319e142751a9c88b104994Jeff Brown            if (ps != null) {
59070825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.addTrace(coords.x, coords.y);
59170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mXVelocity = mVelocity.getXVelocity(id);
59270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mYVelocity = mVelocity.getYVelocity(id);
59385bd0d62830a098c1bdc720dfdcf4fe1b18b657cJeff Brown                mVelocity.getEstimator(id, ps.mEstimator);
5949eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                if (mAltVelocity != null) {
5959eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
5969eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
5979eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                    mAltVelocity.getEstimator(id, ps.mAltEstimator);
5989eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown                }
59970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.mToolType = event.getToolType(i);
60086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright
60186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                if (ps.mHasBoundingBox) {
60286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingLeft = event.getAxisValue(MotionEvent.AXIS_GENERIC_1, i);
60386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingTop = event.getAxisValue(MotionEvent.AXIS_GENERIC_2, i);
60486172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingRight = event.getAxisValue(MotionEvent.AXIS_GENERIC_3, i);
60586172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                    ps.mBoundingBottom = event.getAxisValue(MotionEvent.AXIS_GENERIC_4, i);
60686172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright                }
60770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            }
60870825161b5bf51ed48319e142751a9c88b104994Jeff Brown        }
60970825161b5bf51ed48319e142751a9c88b104994Jeff Brown
61070825161b5bf51ed48319e142751a9c88b104994Jeff Brown        if (action == MotionEvent.ACTION_UP
61170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || action == MotionEvent.ACTION_CANCEL
61270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
61370825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
61470825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
61570825161b5bf51ed48319e142751a9c88b104994Jeff Brown
61670825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final int id = event.getPointerId(index);
61770825161b5bf51ed48319e142751a9c88b104994Jeff Brown            final PointerState ps = mPointers.get(id);
61870825161b5bf51ed48319e142751a9c88b104994Jeff Brown            ps.mCurDown = false;
61933bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
6208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (action == MotionEvent.ACTION_UP
62170825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    || action == MotionEvent.ACTION_CANCEL) {
62270825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurDown = false;
62370825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers = 0;
62470825161b5bf51ed48319e142751a9c88b104994Jeff Brown            } else {
62570825161b5bf51ed48319e142751a9c88b104994Jeff Brown                mCurNumPointers -= 1;
62670825161b5bf51ed48319e142751a9c88b104994Jeff Brown                if (mActivePointerId == id) {
62770825161b5bf51ed48319e142751a9c88b104994Jeff Brown                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
62890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn                }
62970825161b5bf51ed48319e142751a9c88b104994Jeff Brown                ps.addTrace(Float.NaN, Float.NaN);
63090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn            }
63190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        }
63270825161b5bf51ed48319e142751a9c88b104994Jeff Brown
63370825161b5bf51ed48319e142751a9c88b104994Jeff Brown        invalidate();
63490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
63590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn
63690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    @Override
63790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    public boolean onTouchEvent(MotionEvent event) {
63833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        addPointerEvent(event);
639c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
640c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
641c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            requestFocus();
642c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
64390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn        return true;
64490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn    }
6457d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn
6467d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    @Override
64733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    public boolean onGenericMotionEvent(MotionEvent event) {
648c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        final int source = event.getSource();
649c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
65033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            addPointerEvent(event);
651c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
65265fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Joystick", event);
653c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
65465fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Position", event);
655c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        } else {
65665fd251c3913fc921468a3dad190810db19eb9dfJeff Brown            logMotionEvent("Generic", event);
657c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
658c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
659c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
660c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
661c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
662c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyDown(int keyCode, KeyEvent event) {
663c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
664c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            final int repeatCount = event.getRepeatCount();
665c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            if (repeatCount == 0) {
666c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Down: " + event);
667c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            } else {
668c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
669c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            }
67033bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown            return true;
67133bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown        }
672c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyDown(keyCode, event);
673c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
674c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
675c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    @Override
676c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    public boolean onKeyUp(int keyCode, KeyEvent event) {
677c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        if (shouldLogKey(keyCode)) {
678c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            Log.i(TAG, "Key Up: " + event);
679c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            return true;
680c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
681c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return super.onKeyUp(keyCode, event);
682c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    }
683c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown
684c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown    private static boolean shouldLogKey(int keyCode) {
685c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        switch (keyCode) {
686c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_UP:
687c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_DOWN:
688c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_LEFT:
689c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_RIGHT:
690c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            case KeyEvent.KEYCODE_DPAD_CENTER:
691c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return true;
692c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown            default:
693c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                return KeyEvent.isGamepadButton(keyCode)
694c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown                    || KeyEvent.isModifierKey(keyCode);
695c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        }
69633bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    }
69733bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown
69833bbfd2232ea9eaae9a9d87a05a95a430f09bd83Jeff Brown    @Override
6997d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    public boolean onTrackballEvent(MotionEvent event) {
70065fd251c3913fc921468a3dad190810db19eb9dfJeff Brown        logMotionEvent("Trackball", event);
701c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown        return true;
7027d9af5ae762c59e879a86e77dcb330856774bc09Dianne Hackborn    }
703af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
704af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
705af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    protected void onAttachedToWindow() {
706af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        super.onAttachedToWindow();
707af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
708af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm.registerInputDeviceListener(this, getHandler());
709af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDevices();
710af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
711af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
712af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
713af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    protected void onDetachedFromWindow() {
714af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        super.onDetachedFromWindow();
715af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
716af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        mIm.unregisterInputDeviceListener(this);
717af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
718af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
719af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
720af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceAdded(int deviceId) {
721af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Added");
722af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
723af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
724af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
725af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceChanged(int deviceId) {
726af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Changed");
727af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
728af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
729af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    @Override
730af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    public void onInputDeviceRemoved(int deviceId) {
731af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        logInputDeviceState(deviceId, "Device Removed");
732af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
733af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
734af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private void logInputDevices() {
735af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        int[] deviceIds = InputDevice.getDeviceIds();
736af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        for (int i = 0; i < deviceIds.length; i++) {
737af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            logInputDeviceState(deviceIds[i], "Device Enumerated");
738af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        }
739af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
740af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
741af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    private void logInputDeviceState(int deviceId, String state) {
742af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        InputDevice device = mIm.getInputDevice(deviceId);
743af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        if (device != null) {
744af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            Log.i(TAG, state + ": " + device);
745af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        } else {
746af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown            Log.i(TAG, state + ": " + deviceId);
747af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown        }
748af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown    }
749af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown
7508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // HACK
7518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    // A quick and dirty string builder implementation optimized for GC.
752d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // Using String.format causes the application grind to a halt when
753d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // more than a couple of pointers are down due to the number of
754d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown    // temporary objects allocated while formatting strings for drawing or logging.
7558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    private static final class FasterStringBuilder {
7568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private char[] mChars;
7578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int mLength;
7588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder() {
7608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mChars = new char[64];
7618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7638d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder clear() {
7648d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = 0;
7658d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7668d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7678d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7688d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(String value) {
7698d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int valueLength = value.length();
7708d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int index = reserve(valueLength);
7718d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value.getChars(0, valueLength, mChars, index);
7728d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength += valueLength;
7738d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
7748d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7758d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7768d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value) {
7778d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return append(value, 0);
7788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
7798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(int value, int zeroPadWidth) {
7818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final boolean negative = value < 0;
7828d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
7838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = - value;
7848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (value < 0) {
7858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    append("-2147483648");
7868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    return this;
7878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
7888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
7898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int index = reserve(11);
7918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] chars = mChars;
7928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7938d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (value == 0) {
7948d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '0';
7958d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mLength += 1;
7968d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                return this;
7978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
7988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
7998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (negative) {
8008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = '-';
8018d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int divisor = 1000000000;
8048d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int numberWidth = 10;
8058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            while (value < divisor) {
8068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
8078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                numberWidth -= 1;
8088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                if (numberWidth < zeroPadWidth) {
8098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                    chars[index++] = '0';
8108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                }
8118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8128d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            do {
8148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                int digit = value / divisor;
8158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= digit * divisor;
8168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                divisor /= 10;
8178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                chars[index++] = (char) (digit + '0');
8188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            } while (divisor != 0);
8198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8208d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            mLength = index;
8218d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
8228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8238d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8248d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public FasterStringBuilder append(float value, int precision) {
8258d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            int scale = 1;
8268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            for (int i = 0; i < precision; i++) {
8278d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                scale *= 10;
8288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8298d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            value = (float) (Math.rint(value * scale) / scale);
8308d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8318d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            append((int) value);
8328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (precision != 0) {
8348d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append(".");
8358d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value = Math.abs(value);
8368d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                value -= Math.floor(value);
8378d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                append((int) (value * scale), precision);
8388d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8398d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8408d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return this;
8418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8428d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        @Override
8448d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        public String toString() {
8458d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return new String(mChars, 0, mLength);
8468d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown
8488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        private int reserve(int length) {
8498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldLength = mLength;
8508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int newLength = mLength + length;
8518d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final char[] oldChars = mChars;
8528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            final int oldCapacity = oldChars.length;
8538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            if (newLength > oldCapacity) {
8548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final int newCapacity = oldCapacity * 2;
8558d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                final char[] newChars = new char[newCapacity];
8568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                System.arraycopy(oldChars, 0, newChars, 0, oldLength);
8578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown                mChars = newChars;
8588d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            }
8598d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown            return oldLength;
8608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown        }
8618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown    }
86290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn}
863