Home | History | Annotate | Download | only in launcher3
      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.launcher3;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ValueAnimator;
     22 import android.view.View;
     23 
     24 /**
     25  * A convenience class for two-way animations, e.g. a fadeIn/fadeOut animation.
     26  * With a regular ValueAnimator, if you call reverse to show the 'out' animation, you'll get
     27  * a frame-by-frame mirror of the 'in' animation -- i.e., the interpolated values will
     28  * be exactly reversed. Using this class, both the 'in' and the 'out' animation use the
     29  * interpolator in the same direction.
     30  */
     31 public class InterruptibleInOutAnimator {
     32     private long mOriginalDuration;
     33     private float mOriginalFromValue;
     34     private float mOriginalToValue;
     35     private ValueAnimator mAnimator;
     36 
     37     private boolean mFirstRun = true;
     38 
     39     private Object mTag = null;
     40 
     41     private static final int STOPPED = 0;
     42     private static final int IN = 1;
     43     private static final int OUT = 2;
     44 
     45     // TODO: This isn't really necessary, but is here to help diagnose a bug in the drag viz
     46     private int mDirection = STOPPED;
     47 
     48     public InterruptibleInOutAnimator(View view, long duration, float fromValue, float toValue) {
     49         mAnimator = LauncherAnimUtils.ofFloat(view, fromValue, toValue).setDuration(duration);
     50         mOriginalDuration = duration;
     51         mOriginalFromValue = fromValue;
     52         mOriginalToValue = toValue;
     53 
     54         mAnimator.addListener(new AnimatorListenerAdapter() {
     55             @Override
     56             public void onAnimationEnd(Animator animation) {
     57                 mDirection = STOPPED;
     58             }
     59         });
     60     }
     61 
     62     private void animate(int direction) {
     63         final long currentPlayTime = mAnimator.getCurrentPlayTime();
     64         final float toValue = (direction == IN) ? mOriginalToValue : mOriginalFromValue;
     65         final float startValue = mFirstRun ? mOriginalFromValue :
     66                 ((Float) mAnimator.getAnimatedValue()).floatValue();
     67 
     68         // Make sure it's stopped before we modify any values
     69         cancel();
     70 
     71         // TODO: We don't really need to do the animation if startValue == toValue, but
     72         // somehow that doesn't seem to work, possibly a quirk of the animation framework
     73         mDirection = direction;
     74 
     75         // Ensure we don't calculate a non-sensical duration
     76         long duration = mOriginalDuration - currentPlayTime;
     77         mAnimator.setDuration(Math.max(0, Math.min(duration, mOriginalDuration)));
     78 
     79         mAnimator.setFloatValues(startValue, toValue);
     80         mAnimator.start();
     81         mFirstRun = false;
     82     }
     83 
     84     public void cancel() {
     85         mAnimator.cancel();
     86         mDirection = STOPPED;
     87     }
     88 
     89     public void end() {
     90         mAnimator.end();
     91         mDirection = STOPPED;
     92     }
     93 
     94     /**
     95      * Return true when the animation is not running and it hasn't even been started.
     96      */
     97     public boolean isStopped() {
     98         return mDirection == STOPPED;
     99     }
    100 
    101     /**
    102      * This is the equivalent of calling Animator.start(), except that it can be called when
    103      * the animation is running in the opposite direction, in which case we reverse
    104      * direction and animate for a correspondingly shorter duration.
    105      */
    106     public void animateIn() {
    107         animate(IN);
    108     }
    109 
    110     /**
    111      * This is the roughly the equivalent of calling Animator.reverse(), except that it uses the
    112      * same interpolation curve as animateIn(), rather than mirroring it. Also, like animateIn(),
    113      * if the animation is currently running in the opposite direction, we reverse
    114      * direction and animate for a correspondingly shorter duration.
    115      */
    116     public void animateOut() {
    117         animate(OUT);
    118     }
    119 
    120     public void setTag(Object tag) {
    121         mTag = tag;
    122     }
    123 
    124     public Object getTag() {
    125         return mTag;
    126     }
    127 
    128     public ValueAnimator getAnimator() {
    129         return mAnimator;
    130     }
    131 }
    132