private long[] getCurrentPlayTimeByState(State mState) {
   long[] tab = new long[3];
   switch (mState) {
     case ANIMATING_LINE_TO_DOT:
       for (int i = 0; i < mArrowToLineAnimatorSet.getChildAnimations().size(); i++) {
         tab[i] =
             ((ValueAnimator) mArrowToLineAnimatorSet.getChildAnimations().get(i))
                 .getCurrentPlayTime();
       }
       mArrowToLineAnimatorSet.cancel();
       break;
     case ANIMATING_PROGRESS:
       for (int i = 0; i < mProgressAnimationSet.getChildAnimations().size(); i++) {
         tab[i] =
             ((ValueAnimator) mProgressAnimationSet.getChildAnimations().get(i))
                 .getCurrentPlayTime();
       }
       mProgressAnimationSet.cancel();
       break;
     case ANIMATING_ERROR:
       tab[0] = mErrorAnimation.getCurrentPlayTime();
       mErrorAnimation.cancel();
       break;
     case ANIMATING_SUCCESS:
       tab[0] = mSuccessAnimation.getCurrentPlayTime();
       mSuccessAnimation.cancel();
       break;
   }
   return tab;
 }
  static float getAnimatedFraction(ValueAnimator animator) {
    float fraction =
        animator.getDuration() > 0
            ? ((float) animator.getCurrentPlayTime()) / animator.getDuration()
            : 0f;

    fraction = min(fraction, 1f);
    fraction = animator.getInterpolator().getInterpolation(fraction);
    return fraction;
  }
  public void onAnimationUpdate(final ValueAnimator animation) {
    final long currentTime = System.currentTimeMillis();
    if (mStartTime == -1) {
      mStartFrame = sGlobalFrameCounter;
      mStartTime = currentTime;
    }

    final long currentPlayTime = animation.getCurrentPlayTime();
    boolean isFinalFrame = Float.compare(1f, animation.getAnimatedFraction()) == 0;

    if (!mHandlingOnAnimationUpdate
        && sVisible
        &&
        // If the current play time exceeds the duration, or the animated fraction is 1,
        // the animation will get finished, even if we call setCurrentPlayTime -- therefore
        // don't adjust the animation in that case
        currentPlayTime < animation.getDuration()
        && !isFinalFrame) {
      mHandlingOnAnimationUpdate = true;
      long frameNum = sGlobalFrameCounter - mStartFrame;
      // If we haven't drawn our first frame, reset the time to t = 0
      // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
      // are no longer in the foreground and no frames are being rendered ever)
      if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY && currentPlayTime > 0) {
        // The first frame on animations doesn't always trigger an invalidate...
        // force an invalidate here to make sure the animation continues to advance
        mTarget.getRootView().invalidate();
        animation.setCurrentPlayTime(0);
        // For the second frame, if the first frame took more than 16ms,
        // adjust the start time and pretend it took only 16ms anyway. This
        // prevents a large jump in the animation due to an expensive first frame
      } else if (frameNum == 1
          && currentTime < mStartTime + MAX_DELAY
          && !mAdjustedSecondFrameTime
          && currentTime > mStartTime + IDEAL_FRAME_DURATION
          && currentPlayTime > IDEAL_FRAME_DURATION) {
        animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
        mAdjustedSecondFrameTime = true;
      } else {
        if (frameNum > 1) {
          mTarget.post(
              new Runnable() {
                public void run() {
                  animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
                }
              });
        }
        if (DEBUG) print(animation);
      }
      mHandlingOnAnimationUpdate = false;
    } else {
      if (DEBUG) print(animation);
    }
  }
 public void print(ValueAnimator animation) {
   float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
   Log.d(
       "FirstFrameAnimatorHelper",
       sGlobalFrameCounter
           + "("
           + (sGlobalFrameCounter - mStartFrame)
           + ") "
           + mTarget
           + " dirty? "
           + mTarget.isDirty()
           + " "
           + flatFraction
           + " "
           + this
           + " "
           + animation);
 }
  private void animate(int direction) {
    final long currentPlayTime = mAnimator.getCurrentPlayTime();
    final float toValue = (direction == IN) ? mOriginalToValue : mOriginalFromValue;
    final float startValue =
        mFirstRun ? mOriginalFromValue : ((Float) mAnimator.getAnimatedValue()).floatValue();

    // Make sure it's stopped before we modify any values
    cancel();

    mDirection = direction;

    // Ensure we don't calculate a non-sensical duration
    long duration = mOriginalDuration - currentPlayTime;
    mAnimator.setDuration(Math.max(0, Math.min(duration, mOriginalDuration)));

    mAnimator.setFloatValues(startValue, toValue);
    mAnimator.start();
    mFirstRun = false;
  }
  private void animate(int direction) {
    final long currentPlayTime = mAnimator.getCurrentPlayTime();
    final float toValue = (direction == IN) ? mOriginalToValue : mOriginalFromValue;
    final float startValue =
        mFirstRun ? mOriginalFromValue : ((Float) mAnimator.getAnimatedValue()).floatValue();

    // Make sure it's stopped before we modify any values
    cancel();

    // TODO: We don't really need to do the animation if startValue == toValue, but
    // somehow that doesn't seem to work, possibly a quirk of the animation framework
    mDirection = direction;

    // Ensure we don't calculate a non-sensical duration
    long duration = mOriginalDuration - currentPlayTime;
    mAnimator.setDuration(Math.max(0, Math.min(duration, mOriginalDuration)));

    mAnimator.setFloatValues(startValue, toValue);
    mAnimator.start();
    mFirstRun = false;
  }