private int determineTargetPage(float pageOffset, int velocity, int deltaX) {
   int targetPage = mCurItem;
   if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
     if (velocity > 0 && deltaX > 0) {
       targetPage -= 1;
     } else if (velocity < 0 && deltaX < 0) {
       targetPage += 1;
     }
   } else {
     targetPage = (int) Math.round(mCurItem + pageOffset);
   }
   return targetPage;
 }
 @Override
 public void onNestedScroll(
     View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
   if (dyUnconsumed < 0) {
     dyUnconsumed = Math.abs(dyUnconsumed);
     mTotalUnconsumed += dyUnconsumed;
     moveSpinner(mTotalUnconsumed);
   }
   // Dispatch up to the nested parent
   dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dxConsumed, null);
 }
  private void moveSpinner(float overscrollTop) {
    mProgress.showArrow(true);
    float originalDragPercent = overscrollTop / mTotalDragDistance;

    float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
    float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
    float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
    float slingshotDist =
        mUsingCustomStart ? mSpinnerFinalOffset - mOriginalOffsetTop : mSpinnerFinalOffset;
    float tensionSlingshotPercent =
        Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
    float tensionPercent =
        (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f;
    float extraMove = (slingshotDist) * tensionPercent * 2;

    int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
    // where 1.0f is a full circle
    if (mCircleView.getVisibility() != View.VISIBLE) {
      mCircleView.setVisibility(View.VISIBLE);
    }
    if (!mScale) {
      ViewCompat.setScaleX(mCircleView, 1f);
      ViewCompat.setScaleY(mCircleView, 1f);
    }
    if (overscrollTop < mTotalDragDistance) {
      if (mScale) {
        setAnimationProgress(overscrollTop / mTotalDragDistance);
      }
      if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA
          && !isAnimationRunning(mAlphaStartAnimation)) {
        // Animate the alpha
        startProgressAlphaStartAnimation();
      }
      float strokeStart = adjustedPercent * .8f;
      mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
      mProgress.setArrowScale(Math.min(1f, adjustedPercent));
    } else {
      if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) {
        // Animate the alpha
        startProgressAlphaMaxAnimation();
      }
    }
    float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
    mProgress.setProgressRotation(rotation);
    setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */);
  }
 @Override
 public void applyTransformation(float interpolatedTime, Transformation t) {
   int targetTop = 0;
   int endTarget = 0;
   if (!mUsingCustomStart) {
     endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop));
   } else {
     endTarget = (int) mSpinnerFinalOffset; // mSpinnerFinalOffset;
   }
   targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
   int offset = targetTop - mCircleView.getTop();
   setTargetOffsetTopAndBottom(offset, false /* requires update */);
   mProgress.setArrowScale(1 - interpolatedTime);
 }
 private void determineDrag(MotionEvent ev) {
   final int activePointerId = mActivePointerId;
   final int pointerIndex = getPointerIndex(ev, activePointerId);
   if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER) return;
   final float x = MotionEventCompat.getX(ev, pointerIndex);
   final float dx = x - mLastMotionX;
   final float xDiff = Math.abs(dx);
   final float y = MotionEventCompat.getY(ev, pointerIndex);
   final float dy = y - mLastMotionY;
   final float yDiff = Math.abs(dy);
   if (mViewBehind.getbMode()) {
     if (yDiff > (isMenuOpen() ? mTouchSlop / 2 : mTouchSlop)
         && yDiff > xDiff
         && thisSlideAllowed(dy)) {
       startDrag();
       mLastMotionX = x;
       mLastMotionY = y;
       setScrollingCacheEnabled(true);
       // TODO add back in touch slop check
     } else if (yDiff > mTouchSlop) {
       mIsUnableToDrag = true;
     }
   } else {
     if (xDiff > (isMenuOpen() ? mTouchSlop / 2 : mTouchSlop)
         && xDiff > yDiff
         && thisSlideAllowed(dx)) {
       startDrag();
       mLastMotionX = x;
       mLastMotionY = y;
       setScrollingCacheEnabled(true);
       // TODO add back in touch slop check
     } else if (xDiff > mTouchSlop) {
       mIsUnableToDrag = true;
     }
   }
 }
  /**
   * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
   *
   * @param x the number of pixels to scroll by on the X axis
   * @param y the number of pixels to scroll by on the Y axis
   * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
   */
  void smoothScrollTo(int x, int y, int velocity) {
    if (getChildCount() == 0) {
      // Nothing to do.
      setScrollingCacheEnabled(false);
      return;
    }
    int sx = getScrollX();
    int sy = getScrollY();
    int dx = x - sx;
    int dy = y - sy;
    if (dx == 0 && dy == 0) {
      completeScroll();
      if (isMenuOpen()) {
        if (mOpenedListener != null) mOpenedListener.onOpened();
      } else {
        if (mClosedListener != null) mClosedListener.onClosed();
      }
      return;
    }

    setScrollingCacheEnabled(true);
    mScrolling = true;

    final int width = getBehindWidth();
    final int halfWidth = width / 2;
    final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
    final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio);

    int duration = 0;
    velocity = Math.abs(velocity);
    if (velocity > 0) {
      duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
    } else {
      final float pageDelta = (float) Math.abs(dx) / width;
      duration = (int) ((pageDelta + 1) * 100);
      duration = MAX_SETTLE_DURATION;
    }
    duration = Math.min(duration, MAX_SETTLE_DURATION);

    mScroller.startScroll(sx, sy, dx, dy, duration);
    invalidate();
  }
 protected float getPercentOpen() {
   return Math.abs(mScrollX - mContent.getLeft()) / getBehindWidth();
 }
 // We want the duration of the page snap animation to be influenced by the distance that
 // the screen has to travel, however, we don't want this duration to be effected in a
 // purely linear fashion. Instead, we use this method to moderate the effect that the distance
 // of travel has on the overall snap duration.
 float distanceInfluenceForSnapDuration(float f) {
   f -= 0.5f; // center the values about 0.
   f *= 0.3f * Math.PI / 2.0f;
   return (float) Math.sin(f);
 }