FileDocCategorySizeDatePackage
PointerLocationView.javaAPI DocAndroid 5.1 API34970Thu Mar 12 22:22:10 GMT 2015com.android.internal.widget

PointerLocationView

public class PointerLocationView extends android.view.View implements android.hardware.input.InputManager.InputDeviceListener, android.view.WindowManagerPolicy.PointerEventListener

Fields Summary
private static final String
TAG
private static final String
ALT_STRATEGY_PROPERY_KEY
private final int
ESTIMATE_PAST_POINTS
private final int
ESTIMATE_FUTURE_POINTS
private final float
ESTIMATE_INTERVAL
private final android.hardware.input.InputManager
mIm
private final android.view.ViewConfiguration
mVC
private final android.graphics.Paint
mTextPaint
private final android.graphics.Paint
mTextBackgroundPaint
private final android.graphics.Paint
mTextLevelPaint
private final android.graphics.Paint
mPaint
private final android.graphics.Paint
mCurrentPointPaint
private final android.graphics.Paint
mTargetPaint
private final android.graphics.Paint
mPathPaint
private final android.graphics.Paint.FontMetricsInt
mTextMetrics
private int
mHeaderBottom
private boolean
mCurDown
private int
mCurNumPointers
private int
mMaxNumPointers
private int
mActivePointerId
private final ArrayList
mPointers
private final android.view.MotionEvent.PointerCoords
mTempCoords
private final android.view.VelocityTracker
mVelocity
private final android.view.VelocityTracker
mAltVelocity
private final FasterStringBuilder
mText
private boolean
mPrintCoords
private android.graphics.RectF
mReusableOvalRect
Constructors Summary
public PointerLocationView(android.content.Context c)

    
       
        super(c);
        setFocusableInTouchMode(true);

        mIm = (InputManager)c.getSystemService(Context.INPUT_SERVICE);

        mVC = ViewConfiguration.get(c);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(10
                * getResources().getDisplayMetrics().density);
        mTextPaint.setARGB(255, 0, 0, 0);
        mTextBackgroundPaint = new Paint();
        mTextBackgroundPaint.setAntiAlias(false);
        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
        mTextLevelPaint = new Paint();
        mTextLevelPaint.setAntiAlias(false);
        mTextLevelPaint.setARGB(192, 255, 0, 0);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setARGB(255, 255, 255, 255);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        mCurrentPointPaint = new Paint();
        mCurrentPointPaint.setAntiAlias(true);
        mCurrentPointPaint.setARGB(255, 255, 0, 0);
        mCurrentPointPaint.setStyle(Paint.Style.STROKE);
        mCurrentPointPaint.setStrokeWidth(2);
        mTargetPaint = new Paint();
        mTargetPaint.setAntiAlias(false);
        mTargetPaint.setARGB(255, 0, 0, 192);
        mPathPaint = new Paint();
        mPathPaint.setAntiAlias(false);
        mPathPaint.setARGB(255, 0, 96, 255);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(1);
        
        PointerState ps = new PointerState();
        mPointers.add(ps);
        mActivePointerId = 0;
        
        mVelocity = VelocityTracker.obtain();

        String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
        if (altStrategy.length() != 0) {
            Log.d(TAG, "Comparing default velocity tracker strategy with " + altStrategy);
            mAltVelocity = VelocityTracker.obtain(altStrategy);
        } else {
            mAltVelocity = null;
        }
    
Methods Summary
private voiddrawOval(android.graphics.Canvas canvas, float x, float y, float major, float minor, float angle, android.graphics.Paint paint)

               
                
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.rotate((float) (angle * 180 / Math.PI), x, y);
        mReusableOvalRect.left = x - minor / 2;
        mReusableOvalRect.right = x + minor / 2;
        mReusableOvalRect.top = y - major / 2;
        mReusableOvalRect.bottom = y + major / 2;
        canvas.drawOval(mReusableOvalRect, paint);
        canvas.restore();
    
private voidlogCoords(java.lang.String type, int action, int index, android.view.MotionEvent.PointerCoords coords, int id, android.view.MotionEvent event)

        final int toolType = event.getToolType(index);
        final int buttonState = event.getButtonState();
        final String prefix;
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                prefix = "DOWN";
                break;
            case MotionEvent.ACTION_UP:
                prefix = "UP";
                break;
            case MotionEvent.ACTION_MOVE:
                prefix = "MOVE";
                break;
            case MotionEvent.ACTION_CANCEL:
                prefix = "CANCEL";
                break;
            case MotionEvent.ACTION_OUTSIDE:
                prefix = "OUTSIDE";
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
                    prefix = "DOWN";
                } else {
                    prefix = "MOVE";
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                if (index == ((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT)) {
                    prefix = "UP";
                } else {
                    prefix = "MOVE";
                }
                break;
            case MotionEvent.ACTION_HOVER_MOVE:
                prefix = "HOVER MOVE";
                break;
            case MotionEvent.ACTION_HOVER_ENTER:
                prefix = "HOVER ENTER";
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                prefix = "HOVER EXIT";
                break;
            case MotionEvent.ACTION_SCROLL:
                prefix = "SCROLL";
                break;
            default:
                prefix = Integer.toString(action);
                break;
        }

        Log.i(TAG, mText.clear()
                .append(type).append(" id ").append(id + 1)
                .append(": ")
                .append(prefix)
                .append(" (").append(coords.x, 3).append(", ").append(coords.y, 3)
                .append(") Pressure=").append(coords.pressure, 3)
                .append(" Size=").append(coords.size, 3)
                .append(" TouchMajor=").append(coords.touchMajor, 3)
                .append(" TouchMinor=").append(coords.touchMinor, 3)
                .append(" ToolMajor=").append(coords.toolMajor, 3)
                .append(" ToolMinor=").append(coords.toolMinor, 3)
                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
                .append("deg")
                .append(" Tilt=").append((float)(
                        coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
                .append("deg")
                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
                .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
                .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
                .append(" BoundingBox=[(")
                .append(event.getAxisValue(MotionEvent.AXIS_GENERIC_1), 3)
                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_2), 3).append(")")
                .append(", (").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_3), 3)
                .append(", ").append(event.getAxisValue(MotionEvent.AXIS_GENERIC_4), 3)
                .append(")]")
                .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
                .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
                .toString());
    
private voidlogInputDeviceState(int deviceId, java.lang.String state)

        InputDevice device = mIm.getInputDevice(deviceId);
        if (device != null) {
            Log.i(TAG, state + ": " + device);
        } else {
            Log.i(TAG, state + ": " + deviceId);
        }
    
private voidlogInputDevices()

        int[] deviceIds = InputDevice.getDeviceIds();
        for (int i = 0; i < deviceIds.length; i++) {
            logInputDeviceState(deviceIds[i], "Device Enumerated");
        }
    
private voidlogMotionEvent(java.lang.String type, android.view.MotionEvent event)

        final int action = event.getAction();
        final int N = event.getHistorySize();
        final int NI = event.getPointerCount();
        for (int historyPos = 0; historyPos < N; historyPos++) {
            for (int i = 0; i < NI; i++) {
                final int id = event.getPointerId(i);
                event.getHistoricalPointerCoords(i, historyPos, mTempCoords);
                logCoords(type, action, i, mTempCoords, id, event);
            }
        }
        for (int i = 0; i < NI; i++) {
            final int id = event.getPointerId(i);
            event.getPointerCoords(i, mTempCoords);
            logCoords(type, action, i, mTempCoords, id, event);
        }
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();

        mIm.registerInputDeviceListener(this, getHandler());
        logInputDevices();
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();

        mIm.unregisterInputDeviceListener(this);
    
protected voidonDraw(android.graphics.Canvas canvas)

        final int w = getWidth();
        final int itemW = w/7;
        final int base = -mTextMetrics.ascent+1;
        final int bottom = mHeaderBottom;

        final int NP = mPointers.size();

        // Labels
        if (mActivePointerId >= 0) {
            final PointerState ps = mPointers.get(mActivePointerId);
            
            canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
            canvas.drawText(mText.clear()
                    .append("P: ").append(mCurNumPointers)
                    .append(" / ").append(mMaxNumPointers)
                    .toString(), 1, base, mTextPaint);

            final int N = ps.mTraceCount;
            if ((mCurDown && ps.mCurDown) || N == 0) {
                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
                canvas.drawText(mText.clear()
                        .append("X: ").append(ps.mCoords.x, 1)
                        .toString(), 1 + itemW, base, mTextPaint);
                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
                canvas.drawText(mText.clear()
                        .append("Y: ").append(ps.mCoords.y, 1)
                        .toString(), 1 + itemW * 2, base, mTextPaint);
            } else {
                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
                        Math.abs(dx) < mVC.getScaledTouchSlop()
                        ? mTextBackgroundPaint : mTextLevelPaint);
                canvas.drawText(mText.clear()
                        .append("dX: ").append(dx, 1)
                        .toString(), 1 + itemW, base, mTextPaint);
                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
                        Math.abs(dy) < mVC.getScaledTouchSlop()
                        ? mTextBackgroundPaint : mTextLevelPaint);
                canvas.drawText(mText.clear()
                        .append("dY: ").append(dy, 1)
                        .toString(), 1 + itemW * 2, base, mTextPaint);
            }

            canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
            canvas.drawText(mText.clear()
                    .append("Xv: ").append(ps.mXVelocity, 3)
                    .toString(), 1 + itemW * 3, base, mTextPaint);

            canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
            canvas.drawText(mText.clear()
                    .append("Yv: ").append(ps.mYVelocity, 3)
                    .toString(), 1 + itemW * 4, base, mTextPaint);

            canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
            canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
                    bottom, mTextLevelPaint);
            canvas.drawText(mText.clear()
                    .append("Prs: ").append(ps.mCoords.pressure, 2)
                    .toString(), 1 + itemW * 5, base, mTextPaint);

            canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
            canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
                    bottom, mTextLevelPaint);
            canvas.drawText(mText.clear()
                    .append("Size: ").append(ps.mCoords.size, 2)
                    .toString(), 1 + itemW * 6, base, mTextPaint);
        }

        // Pointer trace.
        for (int p = 0; p < NP; p++) {
            final PointerState ps = mPointers.get(p);

            // Draw path.
            final int N = ps.mTraceCount;
            float lastX = 0, lastY = 0;
            boolean haveLast = false;
            boolean drawn = false;
            mPaint.setARGB(255, 128, 255, 255);
            for (int i=0; i < N; i++) {
                float x = ps.mTraceX[i];
                float y = ps.mTraceY[i];
                if (Float.isNaN(x)) {
                    haveLast = false;
                    continue;
                }
                if (haveLast) {
                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
                    final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
                    canvas.drawPoint(lastX, lastY, paint);
                    drawn = true;
                }
                lastX = x;
                lastY = y;
                haveLast = true;
            }

            if (drawn) {
                // Draw movement estimate curve.
                mPaint.setARGB(128, 128, 0, 128);
                float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
                float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
                for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
                    float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
                    float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
                    canvas.drawLine(lx, ly, x, y, mPaint);
                    lx = x;
                    ly = y;
                }

                // Draw velocity vector.
                mPaint.setARGB(255, 255, 64, 128);
                float xVel = ps.mXVelocity * (1000 / 60);
                float yVel = ps.mYVelocity * (1000 / 60);
                canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);

                // Draw alternate estimate.
                if (mAltVelocity != null) {
                    mPaint.setARGB(128, 0, 128, 128);
                    lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
                    ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
                    for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
                        float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL);
                        float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL);
                        canvas.drawLine(lx, ly, x, y, mPaint);
                        lx = x;
                        ly = y;
                    }

                    mPaint.setARGB(255, 64, 255, 128);
                    xVel = ps.mAltXVelocity * (1000 / 60);
                    yVel = ps.mAltYVelocity * (1000 / 60);
                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
                }
            }

            if (mCurDown && ps.mCurDown) {
                // Draw crosshairs.
                canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
                canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);

                // Draw current point.
                int pressureLevel = (int)(ps.mCoords.pressure * 255);
                mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
                canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);

                // Draw current touch ellipse.
                mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
                        ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);

                // Draw current tool ellipse.
                mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
                        ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);

                // Draw the orientation arrow.
                float arrowSize = ps.mCoords.toolMajor * 0.7f;
                if (arrowSize < 20) {
                    arrowSize = 20;
                }
                mPaint.setARGB(255, pressureLevel, 255, 0);
                float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
                        * arrowSize);
                float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
                        * arrowSize);
                if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
                        || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
                    // Show full circle orientation.
                    canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
                            ps.mCoords.x + orientationVectorX,
                            ps.mCoords.y + orientationVectorY,
                            mPaint);
                } else {
                    // Show half circle orientation.
                    canvas.drawLine(
                            ps.mCoords.x - orientationVectorX,
                            ps.mCoords.y - orientationVectorY,
                            ps.mCoords.x + orientationVectorX,
                            ps.mCoords.y + orientationVectorY,
                            mPaint);
                }

                // Draw the tilt point along the orientation arrow.
                float tiltScale = (float) Math.sin(
                        ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
                canvas.drawCircle(
                        ps.mCoords.x + orientationVectorX * tiltScale,
                        ps.mCoords.y + orientationVectorY * tiltScale,
                        3.0f, mPaint);

                // Draw the current bounding box
                if (ps.mHasBoundingBox) {
                    canvas.drawRect(ps.mBoundingLeft, ps.mBoundingTop,
                            ps.mBoundingRight, ps.mBoundingBottom, mPaint);
                }
            }
        }
    
public booleanonGenericMotionEvent(android.view.MotionEvent event)

        final int source = event.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            onPointerEvent(event);
        } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
            logMotionEvent("Joystick", event);
        } else if ((source & InputDevice.SOURCE_CLASS_POSITION) != 0) {
            logMotionEvent("Position", event);
        } else {
            logMotionEvent("Generic", event);
        }
        return true;
    
public voidonInputDeviceAdded(int deviceId)

        logInputDeviceState(deviceId, "Device Added");
    
public voidonInputDeviceChanged(int deviceId)

        logInputDeviceState(deviceId, "Device Changed");
    
public voidonInputDeviceRemoved(int deviceId)

        logInputDeviceState(deviceId, "Device Removed");
    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)

        if (shouldLogKey(keyCode)) {
            final int repeatCount = event.getRepeatCount();
            if (repeatCount == 0) {
                Log.i(TAG, "Key Down: " + event);
            } else {
                Log.i(TAG, "Key Repeat #" + repeatCount + ": " + event);
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    
public booleanonKeyUp(int keyCode, android.view.KeyEvent event)

        if (shouldLogKey(keyCode)) {
            Log.i(TAG, "Key Up: " + event);
            return true;
        }
        return super.onKeyUp(keyCode, event);
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mTextPaint.getFontMetricsInt(mTextMetrics);
        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
        if (false) {
            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
                    + " descent=" + mTextMetrics.descent
                    + " leading=" + mTextMetrics.leading
                    + " top=" + mTextMetrics.top
                    + " bottom=" + mTextMetrics.bottom);
        }
    
public voidonPointerEvent(android.view.MotionEvent event)

        final int action = event.getAction();
        int NP = mPointers.size();

        if (action == MotionEvent.ACTION_DOWN
                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
            if (action == MotionEvent.ACTION_DOWN) {
                for (int p=0; p<NP; p++) {
                    final PointerState ps = mPointers.get(p);
                    ps.clearTrace();
                    ps.mCurDown = false;
                }
                mCurDown = true;
                mCurNumPointers = 0;
                mMaxNumPointers = 0;
                mVelocity.clear();
                if (mAltVelocity != null) {
                    mAltVelocity.clear();
                }
            }

            mCurNumPointers += 1;
            if (mMaxNumPointers < mCurNumPointers) {
                mMaxNumPointers = mCurNumPointers;
            }

            final int id = event.getPointerId(index);
            while (NP <= id) {
                PointerState ps = new PointerState();
                mPointers.add(ps);
                NP++;
            }

            if (mActivePointerId < 0 ||
                    !mPointers.get(mActivePointerId).mCurDown) {
                mActivePointerId = id;
            }

            final PointerState ps = mPointers.get(id);
            ps.mCurDown = true;
            InputDevice device = InputDevice.getDevice(event.getDeviceId());
            ps.mHasBoundingBox = device != null &&
                    device.getMotionRange(MotionEvent.AXIS_GENERIC_1) != null;
        }

        final int NI = event.getPointerCount();

        mVelocity.addMovement(event);
        mVelocity.computeCurrentVelocity(1);
        if (mAltVelocity != null) {
            mAltVelocity.addMovement(event);
            mAltVelocity.computeCurrentVelocity(1);
        }

        final int N = event.getHistorySize();
        for (int historyPos = 0; historyPos < N; historyPos++) {
            for (int i = 0; i < NI; i++) {
                final int id = event.getPointerId(i);
                final PointerState ps = mCurDown ? mPointers.get(id) : null;
                final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
                event.getHistoricalPointerCoords(i, historyPos, coords);
                if (mPrintCoords) {
                    logCoords("Pointer", action, i, coords, id, event);
                }
                if (ps != null) {
                    ps.addTrace(coords.x, coords.y, false);
                }
            }
        }
        for (int i = 0; i < NI; i++) {
            final int id = event.getPointerId(i);
            final PointerState ps = mCurDown ? mPointers.get(id) : null;
            final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
            event.getPointerCoords(i, coords);
            if (mPrintCoords) {
                logCoords("Pointer", action, i, coords, id, event);
            }
            if (ps != null) {
                ps.addTrace(coords.x, coords.y, true);
                ps.mXVelocity = mVelocity.getXVelocity(id);
                ps.mYVelocity = mVelocity.getYVelocity(id);
                mVelocity.getEstimator(id, ps.mEstimator);
                if (mAltVelocity != null) {
                    ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
                    ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
                    mAltVelocity.getEstimator(id, ps.mAltEstimator);
                }
                ps.mToolType = event.getToolType(i);

                if (ps.mHasBoundingBox) {
                    ps.mBoundingLeft = event.getAxisValue(MotionEvent.AXIS_GENERIC_1, i);
                    ps.mBoundingTop = event.getAxisValue(MotionEvent.AXIS_GENERIC_2, i);
                    ps.mBoundingRight = event.getAxisValue(MotionEvent.AXIS_GENERIC_3, i);
                    ps.mBoundingBottom = event.getAxisValue(MotionEvent.AXIS_GENERIC_4, i);
                }
            }
        }

        if (action == MotionEvent.ACTION_UP
                || action == MotionEvent.ACTION_CANCEL
                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP

            final int id = event.getPointerId(index);
            final PointerState ps = mPointers.get(id);
            ps.mCurDown = false;

            if (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_CANCEL) {
                mCurDown = false;
                mCurNumPointers = 0;
            } else {
                mCurNumPointers -= 1;
                if (mActivePointerId == id) {
                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                }
                ps.addTrace(Float.NaN, Float.NaN, false);
            }
        }

        invalidate();
    
public booleanonTouchEvent(android.view.MotionEvent event)

        onPointerEvent(event);

        if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) {
            requestFocus();
        }
        return true;
    
public booleanonTrackballEvent(android.view.MotionEvent event)

        logMotionEvent("Trackball", event);
        return true;
    
public voidsetPrintCoords(boolean state)

        mPrintCoords = state;
    
private static booleanshouldLogKey(int keyCode)

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_UP:
            case KeyEvent.KEYCODE_DPAD_DOWN:
            case KeyEvent.KEYCODE_DPAD_LEFT:
            case KeyEvent.KEYCODE_DPAD_RIGHT:
            case KeyEvent.KEYCODE_DPAD_CENTER:
                return true;
            default:
                return KeyEvent.isGamepadButton(keyCode)
                    || KeyEvent.isModifierKey(keyCode);
        }