private void addIndicatorViews() {
    final Mode mode = getMode();

    if (mode.canPullDown() && null == mIndicatorIvTop) {
      // If the mode can pull down, and we don't have one set already
      mIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_DOWN_TO_REFRESH);
      final FrameLayout.LayoutParams params =
          new FrameLayout.LayoutParams(
              ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
      params.gravity = Gravity.TOP | Gravity.RIGHT;
      mRefreshableViewHolder.addView(mIndicatorIvTop, params);

    } else if (!mode.canPullDown() && null != mIndicatorIvTop) {
      // If we can't pull down, but have a View then remove it
      mRefreshableViewHolder.removeView(mIndicatorIvTop);
      mIndicatorIvTop = null;
    }

    if (mode.canPullUp() && null == mIndicatorIvBottom) {
      // If the mode can pull down, and we don't have one set already
      mIndicatorIvBottom = new IndicatorLayout(getContext(), Mode.PULL_UP_TO_REFRESH);
      final FrameLayout.LayoutParams params =
          new FrameLayout.LayoutParams(
              ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
      params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
      mRefreshableViewHolder.addView(mIndicatorIvBottom, params);

    } else if (!mode.canPullUp() && null != mIndicatorIvBottom) {
      // If we can't pull down, but have a View then remove it
      mRefreshableViewHolder.removeView(mIndicatorIvBottom);
      mIndicatorIvBottom = null;
    }
  }
  /** Re-measure the Loading Views height, and adjust internal padding as necessary */
  private void refreshLoadingViewsHeight() {
    if (mMode.canPullDown()) {
      measureView(mHeaderLayout);
      mHeaderHeight = mHeaderLayout.getMeasuredHeight();
    } else if (mMode.canPullUp()) {
      measureView(mFooterLayout);
      mHeaderHeight = mFooterLayout.getMeasuredHeight();
    } else {
      mHeaderHeight = 0;
    }

    // Hide Loading Views
    switch (mMode) {
      case DISABLED:
        setPadding(0, 0, 0, 0);
      case BOTH:
        setPadding(0, -mHeaderHeight, 0, -mHeaderHeight);
        break;
      case PULL_UP_TO_REFRESH:
        setPadding(0, 0, 0, -mHeaderHeight);
        break;
      case PULL_DOWN_TO_REFRESH:
      default:
        setPadding(0, -mHeaderHeight, 0, 0);
        break;
    }
  }
  /**
   * Updates the View State when the mode has been set. This does not do any checking that the mode
   * is different to current state so always updates.
   */
  protected void updateUIForMode() {
    // Remove Header, and then add Header Loading View again if needed
    if (this == mHeaderLayout.getParent()) {
      removeView(mHeaderLayout);
    }
    if (mMode.canPullDown()) {
      LinearLayout.LayoutParams llp =
          new LayoutParams(
              ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      llp.setMargins(mMargin, 0, mMargin, 0);
      addViewInternal(mHeaderLayout, 0, llp);
    }

    // Remove Footer, and then add Footer Loading View again if needed
    if (this == mFooterLayout.getParent()) {
      removeView(mFooterLayout);
    }
    if (mMode.canPullUp()) {
      LinearLayout.LayoutParams llp =
          new LayoutParams(
              ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      llp.setMargins(mMargin, 0, mMargin, 0);
      addViewInternal(mFooterLayout, 0, llp);
    }

    // Hide Loading Views
    refreshLoadingViewsHeight();

    // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
    // set it to pull down
    mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_DOWN_TO_REFRESH;
  }
 /**
  * Set Text to show when the Widget is being pulled, and will refresh when released
  *
  * @param releaseLabel - String to display
  * @param mode - Controls which Header/Footer Views will be updated. <code>Mode.BOTH</code> will
  *     update all available, other values will update the relevant View.
  */
 public void setReleaseLabel(String releaseLabel, Mode mode) {
   if (null != mHeaderLayout && mode.canPullDown()) {
     mHeaderLayout.setReleaseLabel(releaseLabel);
   }
   if (null != mFooterLayout && mode.canPullUp()) {
     mFooterLayout.setReleaseLabel(releaseLabel);
   }
 }
 @Override
 public void setRefreshingLabel(String refreshingLabel, Mode mode) {
   if (null != mHeaderLayout && mode.canPullDown()) {
     mHeaderLayout.setRefreshingLabel(refreshingLabel);
   }
   if (null != mFooterLayout && mode.canPullUp()) {
     mFooterLayout.setRefreshingLabel(refreshingLabel);
   }
 }
  public void setReleaseLabel(String releaseLabel, Mode mode) {
    super.setReleaseLabel(releaseLabel, mode);

    if (null != mHeaderLoadingView && mode.canPullDown()) {
      mHeaderLoadingView.setReleaseLabel(releaseLabel);
    }
    if (null != mFooterLoadingView && mode.canPullUp()) {
      mFooterLoadingView.setReleaseLabel(releaseLabel);
    }
  }
  /**
   * Set the drawable used in the loading layout.
   *
   * @param drawable - Drawable to display
   * @param mode - Controls which Header/Footer Views will be updated. <code>Mode.BOTH</code> will
   *     update all available, other values will update the relevant View.
   */
  public void setLoadingDrawable(Drawable drawable, Mode mode) {
    if (null != mHeaderLayout && mode.canPullDown()) {
      mHeaderLayout.setLoadingDrawable(drawable);
    }
    if (null != mFooterLayout && mode.canPullUp()) {
      mFooterLayout.setLoadingDrawable(drawable);
    }

    // The Loading Height may have changed, so refresh
    refreshLoadingViewsHeight();
  }
  protected void resetHeader() {
    mState = PULL_TO_REFRESH;
    mIsBeingDragged = false;

    if (mMode.canPullDown()) {
      mHeaderLayout.reset();
    }
    if (mMode.canPullUp()) {
      mFooterLayout.reset();
    }

    smoothScrollTo(0);
  }
  protected void setRefreshingInternal(boolean doScroll) {
    mState = REFRESHING;

    if (mMode.canPullDown()) {
      mHeaderLayout.refreshing();
    }
    if (mMode.canPullUp()) {
      mFooterLayout.refreshing();
    }

    if (doScroll) {
      if (mShowViewWhileRefreshing) {
        smoothScrollTo(mCurrentMode == Mode.PULL_DOWN_TO_REFRESH ? -mHeaderHeight : mHeaderHeight);
      } else {
        smoothScrollTo(0);
      }
    }
  }
  @Override
  public final boolean onInterceptTouchEvent(MotionEvent event) {

    if (!isPullToRefreshEnabled()) {
      return false;
    }

    final int action = event.getAction();

    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
      mIsBeingDragged = false;
      return false;
    }

    if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
      return true;
    }

    switch (action) {
      case MotionEvent.ACTION_MOVE:
        {
          // If we're refreshing, and the flag is set. Eat all MOVE events
          if (mDisableScrollingWhileRefreshing && isRefreshing()) {
            return true;
          }

          if (isReadyForPull()) {
            final float y = event.getY();
            final float dy = y - mLastMotionY;
            final float yDiff = Math.abs(dy);
            final float xDiff = Math.abs(event.getX() - mLastMotionX);

            if (yDiff > mTouchSlop && (!mFilterTouchEvents || yDiff > xDiff)) {
              if (mMode.canPullDown() && dy >= 1f && isReadyForPullDown()) {
                mLastMotionY = y;
                mIsBeingDragged = true;
                if (mMode == Mode.BOTH) {
                  mCurrentMode = Mode.PULL_DOWN_TO_REFRESH;
                }
              } else if (mMode.canPullUp() && dy <= -1f && isReadyForPullUp()) {
                mLastMotionY = y;
                mIsBeingDragged = true;
                if (mMode == Mode.BOTH) {
                  mCurrentMode = Mode.PULL_UP_TO_REFRESH;
                }
              }
            }
          }
          break;
        }
      case MotionEvent.ACTION_DOWN:
        {
          if (isReadyForPull()) {
            mLastMotionY = mInitialMotionY = event.getY();
            mLastMotionX = event.getX();
            mIsBeingDragged = false;
          }
          break;
        }
    }

    return mIsBeingDragged;
  }