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