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