private void animateOffsetToCorrectPosition() {
    mFrom = mCurrentOffsetTop;
    mFromDragPercent = mCurrentDragPercent;

    mAnimateToCorrectPosition.reset();
    mAnimateToCorrectPosition.setDuration(MAX_OFFSET_ANIMATION_DURATION);
    mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
    mRefreshView.clearAnimation();
    mRefreshView.startAnimation(mAnimateToCorrectPosition);

    if (mRefreshing) {
      mBaseRefreshView.start();
      if (mNotify) {
        if (mListener != null) {
          mListener.onRefresh();
        }
      }
    } else {
      mBaseRefreshView.stop();
      animateOffsetToStartPosition();
    }
    mCurrentOffsetTop = mTarget.getTop();
    mTarget.setPadding(
        mTargetPaddingLeft, mTargetPaddingTop, mTargetPaddingRight, mTotalDragDistance);
  }
 private void setTargetOffsetTop(int offset, boolean requiresUpdate) {
   mTarget.offsetTopAndBottom(offset);
   mBaseRefreshView.offsetTopAndBottom(offset);
   mCurrentOffsetTop = mTarget.getTop();
   if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) {
     invalidate();
   }
 }
        @Override
        public void applyTransformation(float interpolatedTime, Transformation t) {
          int targetTop;
          int endTarget = mTotalDragDistance;
          targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
          int offset = targetTop - mTarget.getTop();

          mCurrentDragPercent = mFromDragPercent - (mFromDragPercent - 1.0f) * interpolatedTime;
          mBaseRefreshView.setPercent(mCurrentDragPercent, false);

          setTargetOffsetTop(offset, false /* requires update */);
        }
 private void setRefreshing(boolean refreshing, final boolean notify) {
   if (mRefreshing != refreshing) {
     mNotify = notify;
     ensureTarget();
     mRefreshing = refreshing;
     if (mRefreshing) {
       mBaseRefreshView.setPercent(1f, true);
       animateOffsetToCorrectPosition();
     } else {
       animateOffsetToStartPosition();
     }
   }
 }
  private void moveToStart(float interpolatedTime) {
    int targetTop = mFrom - (int) (mFrom * interpolatedTime);
    float targetPercent = mFromDragPercent * (1.0f - interpolatedTime);
    int offset = targetTop - mTarget.getTop();

    mCurrentDragPercent = targetPercent;
    mBaseRefreshView.setPercent(mCurrentDragPercent, true);
    mTarget.setPadding(
        mTargetPaddingLeft,
        mTargetPaddingTop,
        mTargetPaddingRight,
        mTargetPaddingBottom + targetTop);
    setTargetOffsetTop(offset, false);
  }
 @Override
 public void onAnimationEnd(Animation animation) {
   mBaseRefreshView.stop();
   mCurrentOffsetTop = mTarget.getTop();
 }
  @Override
  public boolean onTouchEvent(@NonNull MotionEvent ev) {

    if (!mIsBeingDragged) {
      return super.onTouchEvent(ev);
    }

    final int action = MotionEventCompat.getActionMasked(ev);

    switch (action) {
      case MotionEvent.ACTION_MOVE:
        {
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          if (pointerIndex < 0) {
            return false;
          }

          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float yDiff = y - mInitialMotionY;
          final float scrollTop = yDiff * DRAG_RATE;
          mCurrentDragPercent = scrollTop / mTotalDragDistance;
          if (mCurrentDragPercent < 0) {
            return false;
          }
          float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
          float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
          float slingshotDist = mTotalDragDistance;
          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 = (int) ((slingshotDist * boundedDragPercent) + extraMove);

          mBaseRefreshView.setPercent(mCurrentDragPercent, true);
          setTargetOffsetTop(targetY - mCurrentOffsetTop, true);
          break;
        }
      case MotionEventCompat.ACTION_POINTER_DOWN:
        final int index = MotionEventCompat.getActionIndex(ev);
        mActivePointerId = MotionEventCompat.getPointerId(ev, index);
        break;
      case MotionEventCompat.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        {
          if (mActivePointerId == INVALID_POINTER) {
            return false;
          }
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE;
          mIsBeingDragged = false;
          if (overScrollTop > mTotalDragDistance) {
            setRefreshing(true, true);
          } else {
            mRefreshing = false;
            animateOffsetToStartPosition();
          }
          mActivePointerId = INVALID_POINTER;
          return false;
        }
    }

    return true;
  }