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