Home | History | Annotate | Download | only in launcher2
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher2;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ValueAnimator;
     22 
     23 /**
     24  * A convenience class for two-way animations, e.g. a fadeIn/fadeOut animation.
     25  * With a regular ValueAnimator, if you call reverse to show the 'out' animation, you'll get
     26  * a frame-by-frame mirror of the 'in' animation -- i.e., the interpolated values will
     27  * be exactly reversed. Using this class, both the 'in' and the 'out' animation use the
     28  * interpolator in the same direction.
     29  */
     30 public class InterruptibleInOutAnimator {
     31     private long mOriginalDuration;
     32     private float mOriginalFromValue;
     33     private float mOriginalToValue;
     34     private ValueAnimator mAnimator;
     35 
     36     private boolean mFirstRun = true;
     37 
     38     private Object mTag = null;
     39 
     40     private static final int STOPPED = 0;
     41     private static final int IN = 1;
     42     private static final int OUT = 2;
     43 
     44     // TODO: This isn't really necessary, but is here to help diagnose a bug in the drag viz
     45     private int mDirection = STOPPED;
     46 
     47     public InterruptibleInOutAnimator(long duration, float fromValue, float toValue) {
     48         mAnimator = ValueAnimator.ofFloat(fromValue, toValue).setDuration(duration);
     49         mOriginalDuration = duration;
     50         mOriginalFromValue = fromValue;
     51         mOriginalToValue = toValue;
     52 
     53         mAnimator.addListener(new AnimatorListenerAdapter() {
     54             @Override
     55             public void onAnimationEnd(Animator animation) {
     56                 mDirection = STOPPED;
     57             }
     58         });
     59     }
     60 
     61     private void animate(int direction) {
     62         final long currentPlayTime = mAnimator.getCurrentPlayTime();
     63         final float toValue = (direction == IN) ? mOriginalToValue : mOriginalFromValue;
     64         final float startValue = mFirstRun ? mOriginalFromValue :
     65                 ((Float) mAnimator.getAnimatedValue()).floatValue();
     66 
     67         // Make sure it's stopped before we modify any values
     68         cancel();
     69 
     70         // TODO: We don't really need to do the animation if startValue == toValue, but
     71         // somehow that doesn't seem to work, possibly a quirk of the animation framework
     72         mDirection = direction;
     73 
     74         // Ensure we don't calculate a non-sensical duration
     75         long duration = mOriginalDuration - currentPlayTime;
     76         mAnimator.setDuration(Math.max(0, Math.min(duration, mOriginalDuration)));
     77 
     78         mAnimator.setFloatValues(startValue, toValue);
     79         mAnimator.start();
     80         mFirstRun = false;
     81     }
     82 
     83     public void cancel() {
     84         mAnimator.cancel();
     85         mDirection = STOPPED;
     86     }
     87 
     88     public void end() {
     89         mAnimator.end();
     90         mDirection = STOPPED;
     91     }
     92 
     93     /**
     94      * Return true when the animation is not running and it hasn't even been started.
     95      */
     96     public boolean isStopped() {
     97         return mDirection == STOPPED;
     98     }
     99 
    100     /**
    101      * This is the equivalent of calling Animator.start(), except that it can be called when
    102      * the animation is running in the opposite direction, in which case we reverse
    103      * direction and animate for a correspondingly shorter duration.
    104      */
    105     public void animateIn() {
    106         animate(IN);
    107     }
    108 
    109     /**
    110      * This is the roughly the equivalent of calling Animator.reverse(), except that it uses the
    111      * same interpolation curve as animateIn(), rather than mirroring it. Also, like animateIn(),
    112      * if the animation is currently running in the opposite direction, we reverse
    113      * direction and animate for a correspondingly shorter duration.
    114      */
    115     public void animateOut() {
    116         animate(OUT);
    117     }
    118 
    119     public void setTag(Object tag) {
    120         mTag = tag;
    121     }
    122 
    123     public Object getTag() {
    124         return mTag;
    125     }
    126 
    127     public ValueAnimator getAnimator() {
    128         return mAnimator;
    129     }
    130 }
    131