1/*
2 * Copyright (C) 2015 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 android.support.design.widget;
18
19import android.os.Handler;
20import android.os.Looper;
21import android.os.SystemClock;
22import android.view.animation.AccelerateDecelerateInterpolator;
23import android.view.animation.Interpolator;
24
25import java.util.ArrayList;
26
27/**
28 * A 'fake' ValueAnimator implementation which uses a Runnable.
29 */
30class ValueAnimatorCompatImplGingerbread extends ValueAnimatorCompat.Impl {
31
32    private static final int HANDLER_DELAY = 10;
33    private static final int DEFAULT_DURATION = 200;
34
35    private static final Handler sHandler = new Handler(Looper.getMainLooper());
36
37    private long mStartTime;
38    private boolean mIsRunning;
39    private float mAnimatedFraction;
40
41    private final int[] mIntValues = new int[2];
42    private final float[] mFloatValues = new float[2];
43
44    private long mDuration = DEFAULT_DURATION;
45    private Interpolator mInterpolator;
46    private ArrayList<AnimatorListenerProxy> mListeners;
47    private ArrayList<AnimatorUpdateListenerProxy> mUpdateListeners;
48
49    private final Runnable mRunnable = new Runnable() {
50        public void run() {
51            update();
52        }
53    };
54
55    @Override
56    public void start() {
57        if (mIsRunning) {
58            // If we're already running, ignore
59            return;
60        }
61        if (mInterpolator == null) {
62            mInterpolator = new AccelerateDecelerateInterpolator();
63        }
64        mIsRunning = true;
65
66        // Reset the animated fraction
67        mAnimatedFraction = 0f;
68
69        startInternal();
70    }
71
72    final void startInternal() {
73        mStartTime = SystemClock.uptimeMillis();
74        dispatchAnimationUpdate();
75        dispatchAnimationStart();
76        // Now start our animation ticker
77        sHandler.postDelayed(mRunnable, HANDLER_DELAY);
78    }
79
80    @Override
81    public boolean isRunning() {
82        return mIsRunning;
83    }
84
85    @Override
86    public void setInterpolator(Interpolator interpolator) {
87        mInterpolator = interpolator;
88    }
89
90    @Override
91    public void addListener(AnimatorListenerProxy listener) {
92        if (mListeners == null) {
93            mListeners = new ArrayList<>();
94        }
95        mListeners.add(listener);
96    }
97
98    @Override
99    public void addUpdateListener(AnimatorUpdateListenerProxy updateListener) {
100        if (mUpdateListeners == null) {
101            mUpdateListeners = new ArrayList<>();
102        }
103        mUpdateListeners.add(updateListener);
104    }
105
106    @Override
107    public void setIntValues(int from, int to) {
108        mIntValues[0] = from;
109        mIntValues[1] = to;
110    }
111
112    @Override
113    public int getAnimatedIntValue() {
114        return AnimationUtils.lerp(mIntValues[0], mIntValues[1], getAnimatedFraction());
115    }
116
117    @Override
118    public void setFloatValues(float from, float to) {
119        mFloatValues[0] = from;
120        mFloatValues[1] = to;
121    }
122
123    @Override
124    public float getAnimatedFloatValue() {
125        return AnimationUtils.lerp(mFloatValues[0], mFloatValues[1], getAnimatedFraction());
126    }
127
128    @Override
129    public void setDuration(long duration) {
130        mDuration = duration;
131    }
132
133    @Override
134    public void cancel() {
135        mIsRunning = false;
136        sHandler.removeCallbacks(mRunnable);
137
138        dispatchAnimationCancel();
139        dispatchAnimationEnd();
140    }
141
142    @Override
143    public float getAnimatedFraction() {
144        return mAnimatedFraction;
145    }
146
147    @Override
148    public void end() {
149        if (mIsRunning) {
150            mIsRunning = false;
151            sHandler.removeCallbacks(mRunnable);
152            // Set our animated fraction to 1
153            mAnimatedFraction = 1f;
154            dispatchAnimationUpdate();
155            dispatchAnimationEnd();
156        }
157    }
158
159    @Override
160    public long getDuration() {
161        return mDuration;
162    }
163
164    final void update() {
165        if (mIsRunning) {
166            // Update the animated fraction
167            final long elapsed = SystemClock.uptimeMillis() - mStartTime;
168            final float linearFraction = MathUtils.constrain(elapsed / (float) mDuration, 0f, 1f);
169            mAnimatedFraction = mInterpolator != null
170                    ? mInterpolator.getInterpolation(linearFraction)
171                    : linearFraction;
172
173            // If we're running, dispatch to the update listeners
174            dispatchAnimationUpdate();
175
176            // Check to see if we've passed the animation duration
177            if (SystemClock.uptimeMillis() >= (mStartTime + mDuration)) {
178                mIsRunning = false;
179
180                dispatchAnimationEnd();
181            }
182        }
183
184        if (mIsRunning) {
185            // If we're still running, post another delayed runnable
186            sHandler.postDelayed(mRunnable, HANDLER_DELAY);
187        }
188    }
189
190    private void dispatchAnimationUpdate() {
191        if (mUpdateListeners != null) {
192            for (int i = 0, count = mUpdateListeners.size(); i < count; i++) {
193                mUpdateListeners.get(i).onAnimationUpdate();
194            }
195        }
196    }
197
198    private void dispatchAnimationStart() {
199        if (mListeners != null) {
200            for (int i = 0, count = mListeners.size(); i < count; i++) {
201                mListeners.get(i).onAnimationStart();
202            }
203        }
204    }
205
206    private void dispatchAnimationCancel() {
207        if (mListeners != null) {
208            for (int i = 0, count = mListeners.size(); i < count; i++) {
209                mListeners.get(i).onAnimationCancel();
210            }
211        }
212    }
213
214    private void dispatchAnimationEnd() {
215        if (mListeners != null) {
216            for (int i = 0, count = mListeners.size(); i < count; i++) {
217                mListeners.get(i).onAnimationEnd();
218            }
219        }
220    }
221}
222