@SuppressLint("ClickableViewAccessibility")
  @Override
  public boolean onTouch(View v, MotionEvent event) {

    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);

    if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
      if (mVelocityTracker != null) {
        mVelocityTracker.recycle();
        mVelocityTracker = null;
      }
    }

    if (mTouchMode == NONE) {
      mGestureDetector.onTouchEvent(event);
    }

    if (!mChart.isDragEnabled() && (!mChart.isScaleXEnabled() && !mChart.isScaleYEnabled()))
      return true;

    // Handle touch events here...
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        startAction(event);

        stopDeceleration();

        saveTouchStart(event);

        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        if (event.getPointerCount() >= 2) {

          mChart.disableScroll();

          saveTouchStart(event);

          // get the distance between the pointers on the x-axis
          mSavedXDist = getXDist(event);

          // get the distance between the pointers on the y-axis
          mSavedYDist = getYDist(event);

          // get the total distance between the pointers
          mSavedDist = spacing(event);

          if (mSavedDist > 10f) {

            if (mChart.isPinchZoomEnabled()) {
              mTouchMode = PINCH_ZOOM;
            } else {
              if (mSavedXDist > mSavedYDist) mTouchMode = X_ZOOM;
              else mTouchMode = Y_ZOOM;
            }
          }

          // determine the touch-pointer center
          midPoint(mTouchPointCenter, event);
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (mTouchMode == DRAG) {

          mChart.disableScroll();
          performDrag(event);

        } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) {

          mChart.disableScroll();

          if (mChart.isScaleXEnabled() || mChart.isScaleYEnabled()) performZoom(event);

        } else if (mTouchMode == NONE
            && Math.abs(
                    distance(event.getX(), mTouchStartPoint.x, event.getY(), mTouchStartPoint.y))
                > 5f) {

          if (mChart.hasNoDragOffset()) {

            if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) {
              mTouchMode = DRAG;
            } else {

              mLastGesture = ChartGesture.DRAG;

              if (mChart.isHighlightPerDragEnabled()) performHighlightDrag(event);
            }

          } else if (mChart.isDragEnabled()) {
            mLastGesture = ChartGesture.DRAG;
            mTouchMode = DRAG;
          }
        }
        break;

      case MotionEvent.ACTION_UP:
        final VelocityTracker velocityTracker = mVelocityTracker;
        final int pointerId = event.getPointerId(0);
        velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity());
        final float velocityY = velocityTracker.getYVelocity(pointerId);
        final float velocityX = velocityTracker.getXVelocity(pointerId);

        if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity()
            || Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) {

          if (mTouchMode == DRAG && mChart.isDragDecelerationEnabled()) {

            stopDeceleration();

            mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
            mDecelerationCurrentPoint = new PointF(event.getX(), event.getY());
            mDecelerationVelocity = new PointF(velocityX, velocityY);

            Utils.postInvalidateOnAnimation(
                mChart); // This causes computeScroll to fire, recommended for this by Google
          }
        }

        if (mTouchMode == X_ZOOM
            || mTouchMode == Y_ZOOM
            || mTouchMode == PINCH_ZOOM
            || mTouchMode == POST_ZOOM) {

          mChart.calculateOffsets();
          mChart.postInvalidate();
        }

        mTouchMode = NONE;
        mChart.enableScroll();

        if (mVelocityTracker != null) {
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }

        endAction(event);

        break;
      case MotionEvent.ACTION_POINTER_UP:
        Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker);

        mTouchMode = POST_ZOOM;
        break;

      case MotionEvent.ACTION_CANCEL:
        mTouchMode = NONE;
        endAction(event);
        break;
    }

    // Perform the transformation, update the chart
    // if (needsRefresh())
    mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true);

    return true; // indicate event was handled
  }