@Override
  public void transformPage(View view, float position) {
    int pageWidth = view.getWidth();

    if (position < -1) { // [-Infinity,-1)
      // This page is way off-screen to the left.
      ViewCompat.setAlpha(view, 0);

    } else if (position <= 0) { // [-1,0]
      // Use the default slide transition when moving to the left page
      ViewCompat.setAlpha(view, 1);
      ViewCompat.setTranslationX(view, 0);
      ViewCompat.setScaleX(view, 1);
      ViewCompat.setScaleY(view, 1);

    } else if (position <= 1) { // (0,1]
      // Fade the page out.
      ViewCompat.setAlpha(view, 1 - position);

      // Counteract the default slide transition
      ViewCompat.setTranslationX(view, pageWidth * -position);

      // Scale the page down (between MIN_SCALE and 1)
      float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
      ViewCompat.setScaleX(view, scaleFactor);
      ViewCompat.setScaleY(view, scaleFactor);

    } else { // (1,+Infinity]
      // This page is way off-screen to the right.
      ViewCompat.setAlpha(view, 0);
    }
  }
 @Override
 protected void onRemoveCanceled(ViewHolder holder) {
   ViewCompat.setRotationY(holder.itemView, 0);
   ViewCompat.setTranslationX(holder.itemView, 0);
   ViewCompat.setScaleX(holder.itemView, 1);
   ViewCompat.setScaleY(holder.itemView, 1);
 }
 private void setAnimationProgress(float progress) {
   if (isAlphaUsedForScale()) {
     setColorViewAlpha((int) (progress * 255.0F));
   } else {
     ViewCompat.setScaleX(this.mCircleView, progress);
     ViewCompat.setScaleY(this.mCircleView, progress);
   }
 }
 @Override
 protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
   // @TODO https://code.google.com/p/android/issues/detail?id=80863
   //        ViewCompat.setPivotY(holder.itemView, holder.itemView.getHeight());
   holder.itemView.setPivotY(holder.itemView.getHeight());
   ViewCompat.setScaleX(holder.itemView, 0);
   ViewCompat.setScaleY(holder.itemView, 0);
 }
  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 */);
  }
  public boolean onTouchEvent(MotionEvent ev) {
    int action = MotionEventCompat.getActionMasked(ev);

    if ((this.mReturningToStart) && (action == 0)) {
      this.mReturningToStart = false;
    }

    if ((!isEnabled()) || (this.mReturningToStart) || (canChildScrollUp())) {
      return false;
    }

    switch (action) {
      case 0:
        this.mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
        this.mIsBeingDragged = false;
        break;
      case 2:
        int pointerIndex = MotionEventCompat.findPointerIndex(ev, this.mActivePointerId);
        if (pointerIndex < 0) {
          Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
          return false;
        }

        float y = MotionEventCompat.getY(ev, pointerIndex);
        float overscrollTop = (y - this.mInitialMotionY) * 0.5F;
        if (this.mIsBeingDragged) {
          this.mProgress.showArrow(true);
          float originalDragPercent = overscrollTop / this.mTotalDragDistance;
          if (originalDragPercent < 0.0F) {
            return false;
          }
          float dragPercent = Math.min(1.0F, Math.abs(originalDragPercent));
          float adjustedPercent = (float) Math.max(dragPercent - 0.4D, 0.0D) * 5.0F / 3.0F;
          float extraOS = Math.abs(overscrollTop) - this.mTotalDragDistance;
          float slingshotDist =
              this.mUsingCustomStart
                  ? this.mSpinnerFinalOffset - this.mOriginalOffsetTop
                  : this.mSpinnerFinalOffset;

          float tensionSlingshotPercent =
              Math.max(0.0F, Math.min(extraOS, slingshotDist * 2.0F) / slingshotDist);

          float tensionPercent =
              (float)
                      (tensionSlingshotPercent / 4.0F
                          - Math.pow(tensionSlingshotPercent / 4.0F, 2.0D))
                  * 2.0F;

          float extraMove = slingshotDist * tensionPercent * 2.0F;

          int targetY = this.mOriginalOffsetTop + (int) (slingshotDist * dragPercent + extraMove);

          if (this.mCircleView.getVisibility() != 0) {
            this.mCircleView.setVisibility(0);
          }
          if (!this.mScale) {
            ViewCompat.setScaleX(this.mCircleView, 1.0F);
            ViewCompat.setScaleY(this.mCircleView, 1.0F);
          }
          if (overscrollTop < this.mTotalDragDistance) {
            if (this.mScale) {
              setAnimationProgress(overscrollTop / this.mTotalDragDistance);
            }
            if ((this.mProgress.getAlpha() > 76)
                && (!isAnimationRunning(this.mAlphaStartAnimation))) {
              startProgressAlphaStartAnimation();
            }
            float strokeStart = adjustedPercent * 0.8F;
            this.mProgress.setStartEndTrim(0.0F, Math.min(0.8F, strokeStart));
            this.mProgress.setArrowScale(Math.min(1.0F, adjustedPercent));
          } else if ((this.mProgress.getAlpha() < 255)
              && (!isAnimationRunning(this.mAlphaMaxAnimation))) {
            startProgressAlphaMaxAnimation();
          }

          float rotation = (-0.25F + 0.4F * adjustedPercent + tensionPercent * 2.0F) * 0.5F;
          this.mProgress.setProgressRotation(rotation);
          setTargetOffsetTopAndBottom(targetY - this.mCurrentTargetOffsetTop, true);
        }
        break;
      case 5:
        int index = MotionEventCompat.getActionIndex(ev);
        this.mActivePointerId = MotionEventCompat.getPointerId(ev, index);
        break;
      case 6:
        onSecondaryPointerUp(ev);
        break;
      case 1:
      case 3:
        if (this.mActivePointerId == -1) {
          if (action == 1) {
            Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
          }
          return false;
        }
        int pointerIndex_1 = MotionEventCompat.findPointerIndex(ev, this.mActivePointerId);
        float y_1 = MotionEventCompat.getY(ev, pointerIndex_1);
        float overscrollTop_1 = (y_1 - this.mInitialMotionY) * 0.5F;
        this.mIsBeingDragged = false;
        if (overscrollTop_1 > this.mTotalDragDistance) {
          setRefreshing(true, true);
        } else {
          this.mRefreshing = false;
          this.mProgress.setStartEndTrim(0.0F, 0.0F);
          Animation.AnimationListener listener = null;
          if (!this.mScale)
            listener =
                new Animation.AnimationListener() {
                  public void onAnimationStart(Animation animation) {}

                  public void onAnimationEnd(Animation animation) {
                    if (!MySwipeRefreshLayout.this.mScale)
                      MySwipeRefreshLayout.this.startScaleDownAnimation(null);
                  }

                  public void onAnimationRepeat(Animation animation) {}
                };
          animateOffsetToStartPosition(this.mCurrentTargetOffsetTop, listener);
          this.mProgress.showArrow(false);
        }
        this.mActivePointerId = -1;
        return false;
      case 4:
    }

    return true;
  }
 protected void preAnimateAdd(RecyclerView.ViewHolder holder) {
   super.preAnimateAdd(holder);
   ViewCompat.setAlpha(holder.itemView, 0);
   ViewCompat.setScaleX(holder.itemView, 1.5f);
   ViewCompat.setScaleY(holder.itemView, 1.5f);
 }
 @Override
 protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
   ViewCompat.setScaleX(holder.itemView, 0);
   ViewCompat.setScaleY(holder.itemView, 0);
 }
 public void handleScale(float scale) {
   scale = 0.1f + 0.9f * scale;
   ViewCompat.setScaleX(mPullDownView, scale);
   ViewCompat.setPivotY(mPullDownView, mPullDownView.getHeight());
   ViewCompat.setScaleY(mPullDownView, scale);
 }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
      mReturningToStart = false;
    }

    if (!isEnabled() || mReturningToStart || canChildScrollUp()) {
      // Fail fast if we're not in a state where a swipe is possible
      return false;
    }

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
        mIsBeingDragged = false;
        break;

      case MotionEvent.ACTION_MOVE:
        {
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          if (pointerIndex < 0) {
            Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
            return false;
          }

          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
          if (mIsBeingDragged) {
            mProgress.showArrow(true);
            float originalDragPercent = overscrollTop / mTotalDragDistance;
            if (originalDragPercent < 0) {
              return false;
            }
            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 */);
          }
          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) {
            if (action == MotionEvent.ACTION_UP) {
              Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
            }
            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 /* notify */);
          } else {
            // cancel refresh
            mRefreshing = false;
            mProgress.setStartEndTrim(0f, 0f);
            Animation.AnimationListener listener = null;
            if (!mScale) {
              listener =
                  new Animation.AnimationListener() {

                    @Override
                    public void onAnimationStart(Animation animation) {}

                    @Override
                    public void onAnimationEnd(Animation animation) {
                      if (!mScale) {
                        startScaleDownAnimation(null);
                      }
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {}
                  };
            }
            animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
            mProgress.showArrow(false);
          }
          mActivePointerId = INVALID_POINTER;
          return false;
        }
    }

    return true;
  }