@Override
  public boolean onTouchEvent(MotionEvent ev) {

    if (!mIsFlippingEnabled) {
      return false;
    }

    if (mPageCount < 1) {
      return false;
    }

    if (!mIsFlipping && !mLastTouchAllowed) {
      return false;
    }

    final int action = ev.getAction();

    if (action == MotionEvent.ACTION_UP
        || action == MotionEvent.ACTION_CANCEL
        || action == MotionEvent.ACTION_OUTSIDE) {
      mLastTouchAllowed = false;
    } else {
      mLastTouchAllowed = true;
    }

    trackVelocity(ev);

    switch (action & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:

        // start flipping immediately if interrupting some sort of animation
        if (endScroll() || endPeak()) {
          mIsFlipping = true;
        }

        // Remember where the motion event started
        mLastX = ev.getX();
        mLastY = ev.getY();
        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
        mFlipDistanceDuringMotionDown = mFlipDistance;
        break;
      case MotionEvent.ACTION_MOVE:
        if (!mIsFlipping) {
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          if (pointerIndex == -1) {
            mActivePointerId = INVALID_POINTER;
            break;
          }
          final float x = MotionEventCompat.getX(ev, pointerIndex);
          final float xDiff = Math.abs(x - mLastX);
          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float yDiff = Math.abs(y - mLastY);
          if ((mIsFlippingVertically && yDiff > mTouchSlop && yDiff > xDiff)
              || (!mIsFlippingVertically && xDiff > mTouchSlop && xDiff > yDiff)) {
            mIsFlipping = true;
            mLastX = x;
            mLastY = y;
          }
        }
        if (mIsFlipping) {
          // Scroll to follow the motion event
          final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          if (activePointerIndex == -1) {
            mActivePointerId = INVALID_POINTER;
            break;
          }
          final float x = MotionEventCompat.getX(ev, activePointerIndex);
          final float deltaX = mLastX - x;
          final float y = MotionEventCompat.getY(ev, activePointerIndex);
          final float deltaY = mLastY - y;
          mLastX = x;
          mLastY = y;

          float deltaFlipDistance = 0;
          if (mIsFlippingVertically) {
            deltaFlipDistance = deltaY;
          } else {
            deltaFlipDistance = deltaX;
          }

          deltaFlipDistance /=
              ((isFlippingVertically() ? getHeight() : getWidth()) / FLIP_DISTANCE_PER_PAGE);
          mFlipDistance += deltaFlipDistance;

          // This code block is used for, if you swipe from page N to page
          // N+1, continue swiping will not trigger page N+2.
          if (mFlipDistance - mFlipDistanceDuringMotionDown >= FLIP_DISTANCE_PER_PAGE) {
            if (deltaFlipDistance > 0) {
              // Undo.
              mFlipDistance -= deltaFlipDistance;
            }
          } else if (mFlipDistance - mFlipDistanceDuringMotionDown <= -FLIP_DISTANCE_PER_PAGE) {
            if (deltaFlipDistance < 0) {
              // Undo.
              mFlipDistance -= deltaFlipDistance;
            }
          }

          final int minFlipDistance = 0;
          final int maxFlipDistance = (mPageCount - 1) * FLIP_DISTANCE_PER_PAGE;
          final boolean isOverFlipping =
              mFlipDistance < minFlipDistance || mFlipDistance > maxFlipDistance;
          if (isOverFlipping) {
            mIsOverFlipping = true;
            mFlipDistance = mOverFlipper.calculate(mFlipDistance, minFlipDistance, maxFlipDistance);
            if (mOnOverFlipListener != null) {
              float overFlip = mOverFlipper.getTotalOverFlip();
              mOnOverFlipListener.onOverFlip(
                  this, mOverFlipMode, overFlip < 0, Math.abs(overFlip), FLIP_DISTANCE_PER_PAGE);
            }
          } else if (mIsOverFlipping) {
            mIsOverFlipping = false;
            if (mOnOverFlipListener != null) {
              // TODO in the future should only notify flip distance 0
              // on the correct edge (previous/next)
              mOnOverFlipListener.onOverFlip(this, mOverFlipMode, false, 0, FLIP_DISTANCE_PER_PAGE);
              mOnOverFlipListener.onOverFlip(this, mOverFlipMode, true, 0, FLIP_DISTANCE_PER_PAGE);
            }
          }

          invalidate();
        }
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        if (mIsFlipping) {
          final VelocityTracker velocityTracker = mVelocityTracker;
          velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

          int velocity = 0;
          if (isFlippingVertically()) {
            velocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker, mActivePointerId);
          } else {
            velocity = (int) VelocityTrackerCompat.getXVelocity(velocityTracker, mActivePointerId);
          }
          smoothFlipTo(getNextPage(velocity));

          mActivePointerId = INVALID_POINTER;
          endFlip();

          mOverFlipper.overFlipEnded();
        }
        break;
      case MotionEventCompat.ACTION_POINTER_DOWN:
        {
          final int index = MotionEventCompat.getActionIndex(ev);
          final float x = MotionEventCompat.getX(ev, index);
          final float y = MotionEventCompat.getY(ev, index);
          mLastX = x;
          mLastY = y;
          mActivePointerId = MotionEventCompat.getPointerId(ev, index);
          break;
        }
      case MotionEventCompat.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        final float x = MotionEventCompat.getX(ev, index);
        final float y = MotionEventCompat.getY(ev, index);
        mLastX = x;
        mLastY = y;
        break;
    }
    if (mActivePointerId == INVALID_POINTER) {
      mLastTouchAllowed = false;
    }
    return true;
  }
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {

    if (!mIsFlippingEnabled) {
      return false;
    }

    if (mPageCount < 1) {
      return false;
    }

    final int action = ev.getAction() & MotionEvent.ACTION_MASK;

    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
      mIsFlipping = false;
      mIsUnableToFlip = false;
      mActivePointerId = INVALID_POINTER;
      if (mVelocityTracker != null) {
        mVelocityTracker.recycle();
        mVelocityTracker = null;
      }
      return false;
    }

    if (action != MotionEvent.ACTION_DOWN) {
      if (mIsFlipping) {
        return true;
      } else if (mIsUnableToFlip) {
        return false;
      }
    }

    switch (action) {
      case MotionEvent.ACTION_MOVE:
        final int activePointerId = mActivePointerId;
        if (activePointerId == INVALID_POINTER) {
          break;
        }

        final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
        if (pointerIndex == -1) {
          mActivePointerId = INVALID_POINTER;
          break;
        }

        final float x = MotionEventCompat.getX(ev, pointerIndex);
        final float dx = x - mLastX;
        final float xDiff = Math.abs(dx);
        final float y = MotionEventCompat.getY(ev, pointerIndex);
        final float dy = y - mLastY;
        final float yDiff = Math.abs(dy);

        if ((mIsFlippingVertically && yDiff > mTouchSlop && yDiff > xDiff)
            || (!mIsFlippingVertically && xDiff > mTouchSlop && xDiff > yDiff)) {
          mIsFlipping = true;
          mLastX = x;
          mLastY = y;
        } else if ((mIsFlippingVertically && xDiff > mTouchSlop)
            || (!mIsFlippingVertically && yDiff > mTouchSlop)) {
          mIsUnableToFlip = true;
        }
        break;

      case MotionEvent.ACTION_DOWN:
        mActivePointerId = ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK;
        mLastX = MotionEventCompat.getX(ev, mActivePointerId);
        mLastY = MotionEventCompat.getY(ev, mActivePointerId);

        mIsFlipping = !mScroller.isFinished() | mPeakAnim != null;
        mIsUnableToFlip = false;
        mLastTouchAllowed = true;

        break;
      case MotionEventCompat.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        break;
    }

    if (!mIsFlipping) {
      trackVelocity(ev);
    }

    return mIsFlipping;
  }