protected void onPtrScrollFinish() {
   if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) {
     if (DEBUG) {
       PtrCLog.d(LOG_TAG, "call onRelease after scroll finish");
     }
     onRelease(true);
   }
 }
  /**
   * Do real refresh work. If there is a hook, execute the hook first.
   *
   * @param ignoreHook
   */
  private void notifyUIRefreshComplete(boolean ignoreHook) {
    /**
     * After hook operation is done, {@link #notifyUIRefreshComplete} will be call in resume action
     * to ignore hook.
     */
    if (mPtrIndicator.hasLeftStartPosition() && !ignoreHook && mRefreshCompleteHook != null) {
      if (DEBUG) {
        PtrCLog.d(LOG_TAG, "notifyUIRefreshComplete mRefreshCompleteHook run.");
      }

      mRefreshCompleteHook.takeOver();
      return;
    }
    if (mPtrUIHandlerHolder.hasHandler()) {
      if (DEBUG) {
        PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshComplete");
      }
      mPtrUIHandlerHolder.onUIRefreshComplete(this);
    }
    mPtrIndicator.onUIRefreshComplete();
    tryScrollBackToTopAfterComplete();
    tryToNotifyReset();
  }
  @Override
  public boolean dispatchTouchEvent(MotionEvent e) {
    if (!isEnabled() || mContent == null || mHeaderView == null) {
      return dispatchTouchEventSupper(e);
    }
    int action = e.getAction();
    switch (action) {
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        mPtrIndicator.onRelease();
        if (mPtrIndicator.hasLeftStartPosition()) {
          if (DEBUG) {
            PtrCLog.d(LOG_TAG, "call onRelease when user release");
          }
          onRelease(false);
          if (mPtrIndicator.hasMovedAfterPressedDown()) {
            sendCancelEvent();
            return true;
          }
          return dispatchTouchEventSupper(e);
        } else {
          return dispatchTouchEventSupper(e);
        }

      case MotionEvent.ACTION_DOWN:
        mHasSendCancelEvent = false;
        mPtrIndicator.onPressDown(e.getX(), e.getY());

        mScrollChecker.abortIfWorking();

        mPreventForHorizontal = false;
        // The cancel event will be sent once the position is moved.
        // So let the event pass to children.
        // fix #93, #102
        dispatchTouchEventSupper(e);
        return true;

      case MotionEvent.ACTION_MOVE:
        mLastMoveEvent = e;
        mPtrIndicator.onMove(e.getX(), e.getY());
        float offsetX = mPtrIndicator.getOffsetX();
        float offsetY = mPtrIndicator.getOffsetY();

        if (mDisableWhenHorizontalMove
            && !mPreventForHorizontal
            && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
          if (mPtrIndicator.isInStartPosition()) {
            mPreventForHorizontal = true;
          }
        }
        if (mPreventForHorizontal) {
          return dispatchTouchEventSupper(e);
        }

        boolean moveDown = offsetY > 0;
        boolean moveUp = !moveDown;
        boolean canMoveUp = mPtrIndicator.hasLeftStartPosition();

        if (DEBUG) {
          boolean canMoveDown =
              mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView);
          PtrCLog.v(
              LOG_TAG,
              "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s",
              offsetY,
              mPtrIndicator.getCurrentPosY(),
              moveUp,
              canMoveUp,
              moveDown,
              canMoveDown);
        }

        // disable move when header not reach top
        if (moveDown
            && mPtrHandler != null
            && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) {
          return dispatchTouchEventSupper(e);
        }

        if ((moveUp && canMoveUp) || moveDown) {
          movePos(offsetY);
          return true;
        }
    }
    return dispatchTouchEventSupper(e);
  }