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