blob: e721de96540fb5ae2bc24f2ffbba6bb0c86cf8ac [file] [log] [blame]
Chet Haasea18a86b2010-09-07 13:20:00 -07001/*
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 android.animation;
18
Tor Norbyec615c6f2015-03-02 10:11:44 -080019import android.annotation.CallSuper;
George Mount7764b922016-01-13 14:51:33 -080020import android.annotation.IntDef;
Chet Haasea18a86b2010-09-07 13:20:00 -070021import android.os.Looper;
Romain Guy18772ea2013-04-10 18:31:22 -070022import android.os.Trace;
Chet Haase2970c492010-11-09 13:58:04 -080023import android.util.AndroidRuntimeException;
Jeff Brownc42b28d2015-04-06 19:49:02 -070024import android.util.Log;
Chet Haasea18a86b2010-09-07 13:20:00 -070025import android.view.animation.AccelerateDecelerateInterpolator;
26import android.view.animation.AnimationUtils;
Chet Haase27c1d4d2010-12-16 07:58:28 -080027import android.view.animation.LinearInterpolator;
Chet Haasea18a86b2010-09-07 13:20:00 -070028
George Mount7764b922016-01-13 14:51:33 -080029import java.lang.annotation.Retention;
30import java.lang.annotation.RetentionPolicy;
Chet Haasea18a86b2010-09-07 13:20:00 -070031import java.util.ArrayList;
32import java.util.HashMap;
33
34/**
35 * This class provides a simple timing engine for running animations
36 * which calculate animated values and set them on target objects.
37 *
38 * <p>There is a single timing pulse that all animations use. It runs in a
39 * custom handler to ensure that property changes happen on the UI thread.</p>
40 *
41 * <p>By default, ValueAnimator uses non-linear time interpolation, via the
42 * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
43 * out of an animation. This behavior can be changed by calling
Chet Haasee0ee2e92010-10-07 09:06:18 -070044 * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.</p>
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080045 *
Chet Haased4307532014-12-01 06:32:38 -080046 * <p>Animators can be created from either code or resource files. Here is an example
47 * of a ValueAnimator resource file:</p>
48 *
49 * {@sample development/samples/ApiDemos/res/anim/animator.xml ValueAnimatorResources}
50 *
51 * <p>It is also possible to use a combination of {@link PropertyValuesHolder} and
52 * {@link Keyframe} resource tags to create a multi-step animation.
53 * Note that you can specify explicit fractional values (from 0 to 1) for
54 * each keyframe to determine when, in the overall duration, the animation should arrive at that
55 * value. Alternatively, you can leave the fractions off and the keyframes will be equally
56 * distributed within the total duration:</p>
57 *
58 * {@sample development/samples/ApiDemos/res/anim/value_animator_pvh_kf.xml
59 * ValueAnimatorKeyframeResources}
60 *
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080061 * <div class="special reference">
62 * <h3>Developer Guides</h3>
63 * <p>For more information about animating with {@code ValueAnimator}, read the
64 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#value-animator">Property
65 * Animation</a> developer guide.</p>
66 * </div>
Chet Haasea18a86b2010-09-07 13:20:00 -070067 */
Romain Guy18772ea2013-04-10 18:31:22 -070068@SuppressWarnings("unchecked")
Doris Liu3618d302015-08-14 11:11:08 -070069public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
Jeff Brownc42b28d2015-04-06 19:49:02 -070070 private static final String TAG = "ValueAnimator";
71 private static final boolean DEBUG = false;
Chet Haasea18a86b2010-09-07 13:20:00 -070072
73 /**
74 * Internal constants
75 */
Chet Haased21a9fe2012-02-01 14:14:17 -080076 private static float sDurationScale = 1.0f;
Chet Haasea18a86b2010-09-07 13:20:00 -070077
Chet Haasea18a86b2010-09-07 13:20:00 -070078 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070079 * Internal variables
80 * NOTE: This object implements the clone() method, making a deep copy of any referenced
81 * objects. As other non-trivial fields are added to this class, make sure to add logic
82 * to clone() to make deep copies of them.
83 */
84
Jeff Brownc42b28d2015-04-06 19:49:02 -070085 /**
86 * The first time that the animation's animateFrame() method is called. This time is used to
87 * determine elapsed time (and therefore the elapsed fraction) in subsequent calls
88 * to animateFrame().
89 *
90 * Whenever mStartTime is set, you must also update mStartTimeCommitted.
91 */
Chet Haase051d35e2010-12-14 07:20:58 -080092 long mStartTime;
Chet Haasea18a86b2010-09-07 13:20:00 -070093
94 /**
Jeff Brownc42b28d2015-04-06 19:49:02 -070095 * When true, the start time has been firmly committed as a chosen reference point in
96 * time by which the progress of the animation will be evaluated. When false, the
97 * start time may be updated when the first animation frame is committed so as
98 * to compensate for jank that may have occurred between when the start time was
99 * initialized and when the frame was actually drawn.
100 *
101 * This flag is generally set to false during the first frame of the animation
102 * when the animation playing state transitions from STOPPED to RUNNING or
103 * resumes after having been paused. This flag is set to true when the start time
104 * is firmly committed and should not be further compensated for jank.
105 */
106 boolean mStartTimeCommitted;
107
108 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700109 * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
110 * to a value.
111 */
Chet Haase0d1c27a2014-11-03 18:35:16 +0000112 float mSeekFraction = -1;
Chet Haasea18a86b2010-09-07 13:20:00 -0700113
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700114 /**
115 * Set on the next frame after pause() is called, used to calculate a new startTime
116 * or delayStartTime which allows the animator to continue from the point at which
117 * it was paused. If negative, has not yet been set.
118 */
119 private long mPauseTime;
120
121 /**
122 * Set when an animator is resumed. This triggers logic in the next frame which
123 * actually resumes the animator.
124 */
125 private boolean mResumed = false;
126
Chet Haasea18a86b2010-09-07 13:20:00 -0700127 // The time interpolator to be used if none is set on the animation
Chet Haasee0ee2e92010-10-07 09:06:18 -0700128 private static final TimeInterpolator sDefaultInterpolator =
129 new AccelerateDecelerateInterpolator();
Chet Haasea18a86b2010-09-07 13:20:00 -0700130
Chet Haasea18a86b2010-09-07 13:20:00 -0700131 /**
Chet Haasef4e3bab2014-12-02 17:51:34 -0800132 * Flag to indicate whether this animator is playing in reverse mode, specifically
133 * by being started or interrupted by a call to reverse(). This flag is different than
134 * mPlayingBackwards, which indicates merely whether the current iteration of the
135 * animator is playing in reverse. It is used in corner cases to determine proper end
136 * behavior.
137 */
138 private boolean mReversing;
139
140 /**
Doris Liufbe94ec2015-09-09 14:52:59 -0700141 * Tracks the overall fraction of the animation, ranging from 0 to mRepeatCount + 1
Chet Haasea18a86b2010-09-07 13:20:00 -0700142 */
Doris Liufbe94ec2015-09-09 14:52:59 -0700143 private float mOverallFraction = 0f;
Chet Haasea18a86b2010-09-07 13:20:00 -0700144
145 /**
Chet Haasea00f3862011-02-22 06:34:40 -0800146 * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction().
Doris Liufbe94ec2015-09-09 14:52:59 -0700147 * This is calculated by interpolating the fraction (range: [0, 1]) in the current iteration.
Chet Haasea00f3862011-02-22 06:34:40 -0800148 */
149 private float mCurrentFraction = 0f;
150
151 /**
Doris Liu3618d302015-08-14 11:11:08 -0700152 * Tracks the time (in milliseconds) when the last frame arrived.
Chet Haasea18a86b2010-09-07 13:20:00 -0700153 */
Doris Liu3618d302015-08-14 11:11:08 -0700154 private long mLastFrameTime = 0;
Chet Haasea18a86b2010-09-07 13:20:00 -0700155
156 /**
Chet Haaseb8f574a2011-08-03 14:10:06 -0700157 * Additional playing state to indicate whether an animator has been start()'d. There is
158 * some lag between a call to start() and the first animation frame. We should still note
159 * that the animation has been started, even if it's first animation frame has not yet
160 * happened, and reflect that state in isRunning().
161 * Note that delayed animations are different: they are not started until their first
162 * animation frame, which occurs after their delay elapses.
163 */
Chet Haase8b699792011-08-05 15:20:19 -0700164 private boolean mRunning = false;
165
166 /**
167 * Additional playing state to indicate whether an animator has been start()'d, whether or
168 * not there is a nonzero startDelay.
169 */
Chet Haaseb8f574a2011-08-03 14:10:06 -0700170 private boolean mStarted = false;
171
172 /**
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700173 * Tracks whether we've notified listeners of the onAnimationStart() event. This can be
Chet Haase17cf42c2012-04-17 13:18:14 -0700174 * complex to keep track of since we notify listeners at different times depending on
175 * startDelay and whether start() was called before end().
176 */
177 private boolean mStartListenersCalled = false;
178
179 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700180 * Flag that denotes whether the animation is set up and ready to go. Used to
181 * set up animation that has not yet been started.
182 */
183 boolean mInitialized = false;
184
Doris Liu3dbaae12015-08-27 14:56:30 -0700185 /**
186 * Flag that tracks whether animation has been requested to end.
187 */
188 private boolean mAnimationEndRequested = false;
189
Chet Haasea18a86b2010-09-07 13:20:00 -0700190 //
191 // Backing variables
192 //
193
194 // How long the animation should last in ms
Doris Liufbe94ec2015-09-09 14:52:59 -0700195 private long mDuration = 300;
Chet Haasea18a86b2010-09-07 13:20:00 -0700196
Doris Liufbe94ec2015-09-09 14:52:59 -0700197 // The amount of time in ms to delay starting the animation after start() is called. Note
198 // that this start delay is unscaled. When there is a duration scale set on the animator, the
199 // scaling factor will be applied to this delay.
200 private long mStartDelay = 0;
Chet Haasea18a86b2010-09-07 13:20:00 -0700201
Chet Haasea18a86b2010-09-07 13:20:00 -0700202 // The number of times the animation will repeat. The default is 0, which means the animation
203 // will play only once
204 private int mRepeatCount = 0;
205
206 /**
207 * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
208 * animation will start from the beginning on every new cycle. REVERSE means the animation
209 * will reverse directions on each iteration.
210 */
211 private int mRepeatMode = RESTART;
212
213 /**
214 * The time interpolator to be used. The elapsed fraction of the animation will be passed
215 * through this interpolator to calculate the interpolated fraction, which is then used to
216 * calculate the animated values.
217 */
Chet Haasee0ee2e92010-10-07 09:06:18 -0700218 private TimeInterpolator mInterpolator = sDefaultInterpolator;
Chet Haasea18a86b2010-09-07 13:20:00 -0700219
220 /**
221 * The set of listeners to be sent events through the life of an animation.
222 */
John Reckd3de42c2014-07-15 14:29:33 -0700223 ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
Chet Haasea18a86b2010-09-07 13:20:00 -0700224
225 /**
226 * The property/value sets being animated.
227 */
228 PropertyValuesHolder[] mValues;
229
230 /**
231 * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
232 * by property name during calls to getAnimatedValue(String).
233 */
234 HashMap<String, PropertyValuesHolder> mValuesMap;
235
236 /**
237 * Public constants
238 */
239
George Mount7764b922016-01-13 14:51:33 -0800240 /** @hide */
241 @IntDef({RESTART, REVERSE})
242 @Retention(RetentionPolicy.SOURCE)
243 public @interface RepeatMode {}
244
Chet Haasea18a86b2010-09-07 13:20:00 -0700245 /**
246 * When the animation reaches the end and <code>repeatCount</code> is INFINITE
247 * or a positive value, the animation restarts from the beginning.
248 */
249 public static final int RESTART = 1;
250 /**
251 * When the animation reaches the end and <code>repeatCount</code> is INFINITE
252 * or a positive value, the animation reverses direction on every iteration.
253 */
254 public static final int REVERSE = 2;
255 /**
256 * This value used used with the {@link #setRepeatCount(int)} property to repeat
257 * the animation indefinitely.
258 */
259 public static final int INFINITE = -1;
260
Chet Haasec38fa1f2012-02-01 16:37:46 -0800261 /**
262 * @hide
263 */
264 public static void setDurationScale(float durationScale) {
265 sDurationScale = durationScale;
266 }
267
Chet Haasea18a86b2010-09-07 13:20:00 -0700268 /**
Jeff Brownff7e6ef2012-08-15 02:05:18 -0700269 * @hide
270 */
271 public static float getDurationScale() {
272 return sDurationScale;
273 }
274
275 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700276 * Creates a new ValueAnimator object. This default constructor is primarily for
Chet Haase2794eb32010-10-12 16:29:28 -0700277 * use internally; the factory methods which take parameters are more generally
Chet Haasea18a86b2010-09-07 13:20:00 -0700278 * useful.
279 */
280 public ValueAnimator() {
281 }
282
283 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700284 * Constructs and returns a ValueAnimator that animates between int values. A single
285 * value implies that that value is the one being animated to. However, this is not typically
286 * useful in a ValueAnimator object because there is no way for the object to determine the
287 * starting value for the animation (unlike ObjectAnimator, which can derive that value
288 * from the target object and property being animated). Therefore, there should typically
289 * be two or more values.
Chet Haasea18a86b2010-09-07 13:20:00 -0700290 *
Chet Haase2794eb32010-10-12 16:29:28 -0700291 * @param values A set of values that the animation will animate between over time.
292 * @return A ValueAnimator object that is set up to animate between the given values.
Chet Haasea18a86b2010-09-07 13:20:00 -0700293 */
Chet Haase2794eb32010-10-12 16:29:28 -0700294 public static ValueAnimator ofInt(int... values) {
295 ValueAnimator anim = new ValueAnimator();
296 anim.setIntValues(values);
297 return anim;
298 }
299
300 /**
George Mount1ffb2802013-10-09 16:13:54 -0700301 * Constructs and returns a ValueAnimator that animates between color values. A single
302 * value implies that that value is the one being animated to. However, this is not typically
303 * useful in a ValueAnimator object because there is no way for the object to determine the
304 * starting value for the animation (unlike ObjectAnimator, which can derive that value
305 * from the target object and property being animated). Therefore, there should typically
306 * be two or more values.
307 *
308 * @param values A set of values that the animation will animate between over time.
309 * @return A ValueAnimator object that is set up to animate between the given values.
310 */
311 public static ValueAnimator ofArgb(int... values) {
312 ValueAnimator anim = new ValueAnimator();
313 anim.setIntValues(values);
314 anim.setEvaluator(ArgbEvaluator.getInstance());
315 return anim;
316 }
317
318 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700319 * Constructs and returns a ValueAnimator that animates between float values. A single
320 * value implies that that value is the one being animated to. However, this is not typically
321 * useful in a ValueAnimator object because there is no way for the object to determine the
322 * starting value for the animation (unlike ObjectAnimator, which can derive that value
323 * from the target object and property being animated). Therefore, there should typically
324 * be two or more values.
325 *
326 * @param values A set of values that the animation will animate between over time.
327 * @return A ValueAnimator object that is set up to animate between the given values.
328 */
329 public static ValueAnimator ofFloat(float... values) {
330 ValueAnimator anim = new ValueAnimator();
331 anim.setFloatValues(values);
332 return anim;
333 }
334
335 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700336 * Constructs and returns a ValueAnimator that animates between the values
337 * specified in the PropertyValuesHolder objects.
338 *
339 * @param values A set of PropertyValuesHolder objects whose values will be animated
340 * between over time.
341 * @return A ValueAnimator object that is set up to animate between the given values.
342 */
343 public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
344 ValueAnimator anim = new ValueAnimator();
345 anim.setValues(values);
346 return anim;
347 }
348 /**
349 * Constructs and returns a ValueAnimator that animates between Object values. A single
350 * value implies that that value is the one being animated to. However, this is not typically
351 * useful in a ValueAnimator object because there is no way for the object to determine the
352 * starting value for the animation (unlike ObjectAnimator, which can derive that value
353 * from the target object and property being animated). Therefore, there should typically
354 * be two or more values.
355 *
356 * <p>Since ValueAnimator does not know how to animate between arbitrary Objects, this
357 * factory method also takes a TypeEvaluator object that the ValueAnimator will use
358 * to perform that interpolation.
359 *
360 * @param evaluator A TypeEvaluator that will be called on each animation frame to
361 * provide the ncessry interpolation between the Object values to derive the animated
362 * value.
363 * @param values A set of values that the animation will animate between over time.
364 * @return A ValueAnimator object that is set up to animate between the given values.
365 */
366 public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
367 ValueAnimator anim = new ValueAnimator();
368 anim.setObjectValues(values);
369 anim.setEvaluator(evaluator);
370 return anim;
371 }
372
373 /**
374 * Sets int values that will be animated between. A single
375 * value implies that that value is the one being animated to. However, this is not typically
376 * useful in a ValueAnimator object because there is no way for the object to determine the
377 * starting value for the animation (unlike ObjectAnimator, which can derive that value
378 * from the target object and property being animated). Therefore, there should typically
379 * be two or more values.
380 *
381 * <p>If there are already multiple sets of values defined for this ValueAnimator via more
382 * than one PropertyValuesHolder object, this method will set the values for the first
383 * of those objects.</p>
384 *
385 * @param values A set of values that the animation will animate between over time.
386 */
387 public void setIntValues(int... values) {
388 if (values == null || values.length == 0) {
389 return;
Chet Haasea18a86b2010-09-07 13:20:00 -0700390 }
Chet Haase2794eb32010-10-12 16:29:28 -0700391 if (mValues == null || mValues.length == 0) {
Romain Guy18772ea2013-04-10 18:31:22 -0700392 setValues(PropertyValuesHolder.ofInt("", values));
Chet Haase2794eb32010-10-12 16:29:28 -0700393 } else {
394 PropertyValuesHolder valuesHolder = mValues[0];
395 valuesHolder.setIntValues(values);
396 }
397 // New property/values/target should cause re-initialization prior to starting
398 mInitialized = false;
399 }
400
401 /**
402 * Sets float values that will be animated between. A single
403 * value implies that that value is the one being animated to. However, this is not typically
404 * useful in a ValueAnimator object because there is no way for the object to determine the
405 * starting value for the animation (unlike ObjectAnimator, which can derive that value
406 * from the target object and property being animated). Therefore, there should typically
407 * be two or more values.
408 *
409 * <p>If there are already multiple sets of values defined for this ValueAnimator via more
410 * than one PropertyValuesHolder object, this method will set the values for the first
411 * of those objects.</p>
412 *
413 * @param values A set of values that the animation will animate between over time.
414 */
415 public void setFloatValues(float... values) {
416 if (values == null || values.length == 0) {
417 return;
418 }
419 if (mValues == null || mValues.length == 0) {
Romain Guy18772ea2013-04-10 18:31:22 -0700420 setValues(PropertyValuesHolder.ofFloat("", values));
Chet Haase2794eb32010-10-12 16:29:28 -0700421 } else {
422 PropertyValuesHolder valuesHolder = mValues[0];
423 valuesHolder.setFloatValues(values);
424 }
425 // New property/values/target should cause re-initialization prior to starting
426 mInitialized = false;
427 }
428
429 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700430 * Sets the values to animate between for this animation. A single
431 * value implies that that value is the one being animated to. However, this is not typically
432 * useful in a ValueAnimator object because there is no way for the object to determine the
433 * starting value for the animation (unlike ObjectAnimator, which can derive that value
434 * from the target object and property being animated). Therefore, there should typically
435 * be two or more values.
436 *
437 * <p>If there are already multiple sets of values defined for this ValueAnimator via more
438 * than one PropertyValuesHolder object, this method will set the values for the first
439 * of those objects.</p>
440 *
441 * <p>There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate
442 * between these value objects. ValueAnimator only knows how to interpolate between the
443 * primitive types specified in the other setValues() methods.</p>
444 *
445 * @param values The set of values to animate between.
446 */
447 public void setObjectValues(Object... values) {
448 if (values == null || values.length == 0) {
449 return;
450 }
451 if (mValues == null || mValues.length == 0) {
Romain Guy18772ea2013-04-10 18:31:22 -0700452 setValues(PropertyValuesHolder.ofObject("", null, values));
Chet Haase2794eb32010-10-12 16:29:28 -0700453 } else {
454 PropertyValuesHolder valuesHolder = mValues[0];
455 valuesHolder.setObjectValues(values);
456 }
457 // New property/values/target should cause re-initialization prior to starting
458 mInitialized = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700459 }
460
461 /**
462 * Sets the values, per property, being animated between. This function is called internally
Ken Wakasaf76a50c2012-03-09 19:56:35 +0900463 * by the constructors of ValueAnimator that take a list of values. But a ValueAnimator can
Chet Haasea18a86b2010-09-07 13:20:00 -0700464 * be constructed without values and this method can be called to set the values manually
465 * instead.
466 *
467 * @param values The set of values, per property, being animated between.
468 */
469 public void setValues(PropertyValuesHolder... values) {
470 int numValues = values.length;
471 mValues = values;
472 mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
473 for (int i = 0; i < numValues; ++i) {
Romain Guy18772ea2013-04-10 18:31:22 -0700474 PropertyValuesHolder valuesHolder = values[i];
Chet Haasea18a86b2010-09-07 13:20:00 -0700475 mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
476 }
Chet Haase0e0590b2010-09-26 11:57:28 -0700477 // New property/values/target should cause re-initialization prior to starting
478 mInitialized = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700479 }
480
481 /**
482 * Returns the values that this ValueAnimator animates between. These values are stored in
483 * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
484 * of value objects instead.
485 *
486 * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
487 * values, per property, that define the animation.
488 */
489 public PropertyValuesHolder[] getValues() {
490 return mValues;
491 }
492
493 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700494 * This function is called immediately before processing the first animation
495 * frame of an animation. If there is a nonzero <code>startDelay</code>, the
496 * function is called after that delay ends.
497 * It takes care of the final initialization steps for the
498 * animation.
499 *
500 * <p>Overrides of this method should call the superclass method to ensure
501 * that internal mechanisms for the animation are set up correctly.</p>
502 */
Tor Norbyec615c6f2015-03-02 10:11:44 -0800503 @CallSuper
Chet Haasea18a86b2010-09-07 13:20:00 -0700504 void initAnimation() {
505 if (!mInitialized) {
506 int numValues = mValues.length;
507 for (int i = 0; i < numValues; ++i) {
508 mValues[i].init();
509 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700510 mInitialized = true;
511 }
512 }
513
Chet Haasea18a86b2010-09-07 13:20:00 -0700514 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700515 * Sets the length of the animation. The default duration is 300 milliseconds.
Chet Haasea18a86b2010-09-07 13:20:00 -0700516 *
Chet Haase27c1d4d2010-12-16 07:58:28 -0800517 * @param duration The length of the animation, in milliseconds. This value cannot
518 * be negative.
Chet Haase2794eb32010-10-12 16:29:28 -0700519 * @return ValueAnimator The object called with setDuration(). This return
520 * value makes it easier to compose statements together that construct and then set the
521 * duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
Chet Haasea18a86b2010-09-07 13:20:00 -0700522 */
Jeff Brownc42b28d2015-04-06 19:49:02 -0700523 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700524 public ValueAnimator setDuration(long duration) {
Chet Haase27c1d4d2010-12-16 07:58:28 -0800525 if (duration < 0) {
526 throw new IllegalArgumentException("Animators cannot have negative duration: " +
527 duration);
528 }
Doris Liufbe94ec2015-09-09 14:52:59 -0700529 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700530 return this;
Chet Haasea18a86b2010-09-07 13:20:00 -0700531 }
532
Doris Liufbe94ec2015-09-09 14:52:59 -0700533 private long getScaledDuration() {
534 return (long)(mDuration * sDurationScale);
Jeff Brown7a08fe02014-10-07 15:55:35 -0700535 }
536
Chet Haasea18a86b2010-09-07 13:20:00 -0700537 /**
Chet Haase2794eb32010-10-12 16:29:28 -0700538 * Gets the length of the animation. The default duration is 300 milliseconds.
Chet Haasea18a86b2010-09-07 13:20:00 -0700539 *
540 * @return The length of the animation, in milliseconds.
541 */
Jeff Brownc42b28d2015-04-06 19:49:02 -0700542 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700543 public long getDuration() {
Doris Liufbe94ec2015-09-09 14:52:59 -0700544 return mDuration;
Chet Haasea18a86b2010-09-07 13:20:00 -0700545 }
546
Doris Liu13099142015-07-10 17:32:41 -0700547 @Override
548 public long getTotalDuration() {
549 if (mRepeatCount == INFINITE) {
550 return DURATION_INFINITE;
551 } else {
Doris Liufbe94ec2015-09-09 14:52:59 -0700552 return mStartDelay + (mDuration * (mRepeatCount + 1));
Doris Liu13099142015-07-10 17:32:41 -0700553 }
554 }
555
556 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700557 * Sets the position of the animation to the specified point in time. This time should
558 * be between 0 and the total duration of the animation, including any repetition. If
559 * the animation has not yet been started, then it will not advance forward after it is
560 * set to this time; it will simply set the time to this value and perform any appropriate
561 * actions based on that time. If the animation is already running, then setCurrentPlayTime()
562 * will set the current playing time to this value and continue playing from that point.
563 *
564 * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
565 */
566 public void setCurrentPlayTime(long playTime) {
Doris Liufbe94ec2015-09-09 14:52:59 -0700567 float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
Chet Haase0d1c27a2014-11-03 18:35:16 +0000568 setCurrentFraction(fraction);
569 }
570
571 /**
572 * Sets the position of the animation to the specified fraction. This fraction should
573 * be between 0 and the total fraction of the animation, including any repetition. That is,
574 * a fraction of 0 will position the animation at the beginning, a value of 1 at the end,
Chet Haasef4e3bab2014-12-02 17:51:34 -0800575 * and a value of 2 at the end of a reversing animator that repeats once. If
Chet Haase0d1c27a2014-11-03 18:35:16 +0000576 * the animation has not yet been started, then it will not advance forward after it is
577 * set to this fraction; it will simply set the fraction to this value and perform any
578 * appropriate actions based on that fraction. If the animation is already running, then
579 * setCurrentFraction() will set the current fraction to this value and continue
Eino-Ville Talvala5a5afe02014-12-05 11:11:45 -0800580 * playing from that point. {@link Animator.AnimatorListener} events are not called
Chet Haasef4e3bab2014-12-02 17:51:34 -0800581 * due to changing the fraction; those events are only processed while the animation
582 * is running.
Chet Haase0d1c27a2014-11-03 18:35:16 +0000583 *
Chet Haasef4e3bab2014-12-02 17:51:34 -0800584 * @param fraction The fraction to which the animation is advanced or rewound. Values
585 * outside the range of 0 to the maximum fraction for the animator will be clamped to
586 * the correct range.
Chet Haase0d1c27a2014-11-03 18:35:16 +0000587 */
588 public void setCurrentFraction(float fraction) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700589 initAnimation();
Doris Liufbe94ec2015-09-09 14:52:59 -0700590 fraction = clampFraction(fraction);
591 long seekTime = (long) (getScaledDuration() * fraction);
Chet Haasef4e3bab2014-12-02 17:51:34 -0800592 long currentTime = AnimationUtils.currentAnimationTimeMillis();
593 mStartTime = currentTime - seekTime;
Jeff Brownc42b28d2015-04-06 19:49:02 -0700594 mStartTimeCommitted = true; // do not allow start time to be compensated for jank
Doris Liu3618d302015-08-14 11:11:08 -0700595 if (!mRunning) {
Chet Haase0d1c27a2014-11-03 18:35:16 +0000596 mSeekFraction = fraction;
Chet Haasea18a86b2010-09-07 13:20:00 -0700597 }
Doris Liufbe94ec2015-09-09 14:52:59 -0700598 mOverallFraction = fraction;
599 final float currentIterationFraction = getCurrentIterationFraction(fraction);
600 animateValue(currentIterationFraction);
601 }
602
603 /**
604 * Calculates current iteration based on the overall fraction. The overall fraction will be
605 * in the range of [0, mRepeatCount + 1]. Both current iteration and fraction in the current
606 * iteration can be derived from it.
607 */
608 private int getCurrentIteration(float fraction) {
609 fraction = clampFraction(fraction);
610 // If the overall fraction is a positive integer, we consider the current iteration to be
611 // complete. In other words, the fraction for the current iteration would be 1, and the
612 // current iteration would be overall fraction - 1.
613 double iteration = Math.floor(fraction);
614 if (fraction == iteration && fraction > 0) {
615 iteration--;
Chet Haasef4e3bab2014-12-02 17:51:34 -0800616 }
Doris Liufbe94ec2015-09-09 14:52:59 -0700617 return (int) iteration;
618 }
619
620 /**
621 * Calculates the fraction of the current iteration, taking into account whether the animation
622 * should be played backwards. E.g. When the animation is played backwards in an iteration,
623 * the fraction for that iteration will go from 1f to 0f.
624 */
625 private float getCurrentIterationFraction(float fraction) {
626 fraction = clampFraction(fraction);
627 int iteration = getCurrentIteration(fraction);
628 float currentFraction = fraction - iteration;
629 return shouldPlayBackward(iteration) ? 1f - currentFraction : currentFraction;
630 }
631
632 /**
633 * Clamps fraction into the correct range: [0, mRepeatCount + 1]. If repeat count is infinite,
634 * no upper bound will be set for the fraction.
635 *
636 * @param fraction fraction to be clamped
637 * @return fraction clamped into the range of [0, mRepeatCount + 1]
638 */
639 private float clampFraction(float fraction) {
640 if (fraction < 0) {
641 fraction = 0;
642 } else if (mRepeatCount != INFINITE) {
643 fraction = Math.min(fraction, mRepeatCount + 1);
644 }
645 return fraction;
646 }
647
648 /**
649 * Calculates the direction of animation playing (i.e. forward or backward), based on 1)
650 * whether the entire animation is being reversed, 2) repeat mode applied to the current
651 * iteration.
652 */
653 private boolean shouldPlayBackward(int iteration) {
654 if (iteration > 0 && mRepeatMode == REVERSE &&
655 (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
656 // if we were seeked to some other iteration in a reversing animator,
657 // figure out the correct direction to start playing based on the iteration
658 if (mReversing) {
659 return (iteration % 2) == 0;
660 } else {
661 return (iteration % 2) != 0;
662 }
663 } else {
664 return mReversing;
665 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700666 }
667
668 /**
669 * Gets the current position of the animation in time, which is equal to the current
670 * time minus the time that the animation started. An animation that is not yet started will
Chet Haase4365e5a2015-10-06 19:32:57 -0700671 * return a value of zero, unless the animation has has its play time set via
672 * {@link #setCurrentPlayTime(long)} or {@link #setCurrentFraction(float)}, in which case
673 * it will return the time that was set.
Chet Haasea18a86b2010-09-07 13:20:00 -0700674 *
675 * @return The current position in time of the animation.
676 */
677 public long getCurrentPlayTime() {
Chet Haase4365e5a2015-10-06 19:32:57 -0700678 if (!mInitialized || (!mStarted && mSeekFraction < 0)) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700679 return 0;
680 }
Chet Haase4365e5a2015-10-06 19:32:57 -0700681 if (mSeekFraction >= 0) {
Doris Liufbe94ec2015-09-09 14:52:59 -0700682 return (long) (mDuration * mSeekFraction);
Chet Haase4365e5a2015-10-06 19:32:57 -0700683 }
Doris Liufbe94ec2015-09-09 14:52:59 -0700684 float durationScale = sDurationScale == 0 ? 1 : sDurationScale;
685 return (long) ((AnimationUtils.currentAnimationTimeMillis() - mStartTime) / durationScale);
Chet Haasea18a86b2010-09-07 13:20:00 -0700686 }
687
688 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700689 * The amount of time, in milliseconds, to delay starting the animation after
690 * {@link #start()} is called.
691 *
692 * @return the number of milliseconds to delay running the animation
693 */
Jeff Brownc42b28d2015-04-06 19:49:02 -0700694 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700695 public long getStartDelay() {
Doris Liufbe94ec2015-09-09 14:52:59 -0700696 return mStartDelay;
Chet Haasea18a86b2010-09-07 13:20:00 -0700697 }
698
699 /**
700 * The amount of time, in milliseconds, to delay starting the animation after
701 * {@link #start()} is called.
702
703 * @param startDelay The amount of the delay, in milliseconds
704 */
Jeff Brownc42b28d2015-04-06 19:49:02 -0700705 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700706 public void setStartDelay(long startDelay) {
Doris Liufbe94ec2015-09-09 14:52:59 -0700707 mStartDelay = startDelay;
Chet Haasea18a86b2010-09-07 13:20:00 -0700708 }
709
710 /**
711 * The amount of time, in milliseconds, between each frame of the animation. This is a
712 * requested time that the animation will attempt to honor, but the actual delay between
713 * frames may be different, depending on system load and capabilities. This is a static
714 * function because the same delay will be applied to all animations, since they are all
715 * run off of a single timing loop.
716 *
Jeff Brown96e942d2011-11-30 19:55:01 -0800717 * The frame delay may be ignored when the animation system uses an external timing
718 * source, such as the display refresh rate (vsync), to govern animations.
719 *
Doris Liu2b2e2c82015-10-01 11:08:54 -0700720 * Note that this method should be called from the same thread that {@link #start()} is
721 * called in order to check the frame delay for that animation. A runtime exception will be
722 * thrown if the calling thread does not have a Looper.
723 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700724 * @return the requested time between frames, in milliseconds
725 */
726 public static long getFrameDelay() {
Doris Liu3618d302015-08-14 11:11:08 -0700727 return AnimationHandler.getInstance().getFrameDelay();
Chet Haasea18a86b2010-09-07 13:20:00 -0700728 }
729
730 /**
731 * The amount of time, in milliseconds, between each frame of the animation. This is a
732 * requested time that the animation will attempt to honor, but the actual delay between
733 * frames may be different, depending on system load and capabilities. This is a static
734 * function because the same delay will be applied to all animations, since they are all
735 * run off of a single timing loop.
736 *
Jeff Brown96e942d2011-11-30 19:55:01 -0800737 * The frame delay may be ignored when the animation system uses an external timing
738 * source, such as the display refresh rate (vsync), to govern animations.
739 *
Doris Liu2b2e2c82015-10-01 11:08:54 -0700740 * Note that this method should be called from the same thread that {@link #start()} is
741 * called in order to have the new frame delay take effect on that animation. A runtime
742 * exception will be thrown if the calling thread does not have a Looper.
743 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700744 * @param frameDelay the requested time between frames, in milliseconds
745 */
746 public static void setFrameDelay(long frameDelay) {
Doris Liu3618d302015-08-14 11:11:08 -0700747 AnimationHandler.getInstance().setFrameDelay(frameDelay);
Chet Haasea18a86b2010-09-07 13:20:00 -0700748 }
749
750 /**
751 * The most recent value calculated by this <code>ValueAnimator</code> when there is just one
752 * property being animated. This value is only sensible while the animation is running. The main
753 * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code>
754 * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
755 * is called during each animation frame, immediately after the value is calculated.
756 *
757 * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for
758 * the single property being animated. If there are several properties being animated
759 * (specified by several PropertyValuesHolder objects in the constructor), this function
760 * returns the animated value for the first of those objects.
761 */
762 public Object getAnimatedValue() {
763 if (mValues != null && mValues.length > 0) {
764 return mValues[0].getAnimatedValue();
765 }
766 // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
767 return null;
768 }
769
770 /**
771 * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>.
772 * The main purpose for this read-only property is to retrieve the value from the
773 * <code>ValueAnimator</code> during a call to
774 * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
775 * is called during each animation frame, immediately after the value is calculated.
776 *
777 * @return animatedValue The value most recently calculated for the named property
778 * by this <code>ValueAnimator</code>.
779 */
780 public Object getAnimatedValue(String propertyName) {
781 PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
782 if (valuesHolder != null) {
783 return valuesHolder.getAnimatedValue();
784 } else {
785 // At least avoid crashing if called with bogus propertyName
786 return null;
787 }
788 }
789
790 /**
791 * Sets how many times the animation should be repeated. If the repeat
792 * count is 0, the animation is never repeated. If the repeat count is
793 * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
794 * into account. The repeat count is 0 by default.
795 *
796 * @param value the number of times the animation should be repeated
797 */
798 public void setRepeatCount(int value) {
799 mRepeatCount = value;
800 }
801 /**
802 * Defines how many times the animation should repeat. The default value
803 * is 0.
804 *
805 * @return the number of times the animation should repeat, or {@link #INFINITE}
806 */
807 public int getRepeatCount() {
808 return mRepeatCount;
809 }
810
811 /**
812 * Defines what this animation should do when it reaches the end. This
813 * setting is applied only when the repeat count is either greater than
814 * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
815 *
816 * @param value {@link #RESTART} or {@link #REVERSE}
817 */
George Mount7764b922016-01-13 14:51:33 -0800818 public void setRepeatMode(@RepeatMode int value) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700819 mRepeatMode = value;
820 }
821
822 /**
823 * Defines what this animation should do when it reaches the end.
824 *
825 * @return either one of {@link #REVERSE} or {@link #RESTART}
826 */
George Mount7764b922016-01-13 14:51:33 -0800827 @RepeatMode
Chet Haasea18a86b2010-09-07 13:20:00 -0700828 public int getRepeatMode() {
829 return mRepeatMode;
830 }
831
832 /**
833 * Adds a listener to the set of listeners that are sent update events through the life of
834 * an animation. This method is called on all listeners for every frame of the animation,
835 * after the values for the animation have been calculated.
836 *
837 * @param listener the listener to be added to the current set of listeners for this animation.
838 */
839 public void addUpdateListener(AnimatorUpdateListener listener) {
840 if (mUpdateListeners == null) {
841 mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
842 }
843 mUpdateListeners.add(listener);
844 }
845
846 /**
Jim Miller30604212010-09-22 19:56:23 -0700847 * Removes all listeners from the set listening to frame updates for this animation.
848 */
849 public void removeAllUpdateListeners() {
850 if (mUpdateListeners == null) {
851 return;
852 }
853 mUpdateListeners.clear();
854 mUpdateListeners = null;
855 }
856
857 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700858 * Removes a listener from the set listening to frame updates for this animation.
859 *
860 * @param listener the listener to be removed from the current set of update listeners
861 * for this animation.
862 */
863 public void removeUpdateListener(AnimatorUpdateListener listener) {
864 if (mUpdateListeners == null) {
865 return;
866 }
867 mUpdateListeners.remove(listener);
868 if (mUpdateListeners.size() == 0) {
869 mUpdateListeners = null;
870 }
871 }
872
873
874 /**
875 * The time interpolator used in calculating the elapsed fraction of this animation. The
876 * interpolator determines whether the animation runs with linear or non-linear motion,
877 * such as acceleration and deceleration. The default value is
878 * {@link android.view.animation.AccelerateDecelerateInterpolator}
879 *
Chet Haase27c1d4d2010-12-16 07:58:28 -0800880 * @param value the interpolator to be used by this animation. A value of <code>null</code>
881 * will result in linear interpolation.
Chet Haasea18a86b2010-09-07 13:20:00 -0700882 */
883 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700884 public void setInterpolator(TimeInterpolator value) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700885 if (value != null) {
886 mInterpolator = value;
Chet Haase27c1d4d2010-12-16 07:58:28 -0800887 } else {
888 mInterpolator = new LinearInterpolator();
Chet Haasea18a86b2010-09-07 13:20:00 -0700889 }
890 }
891
892 /**
893 * Returns the timing interpolator that this ValueAnimator uses.
894 *
895 * @return The timing interpolator for this ValueAnimator.
896 */
Chet Haase430742f2013-04-12 11:18:36 -0700897 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700898 public TimeInterpolator getInterpolator() {
Chet Haasea18a86b2010-09-07 13:20:00 -0700899 return mInterpolator;
900 }
901
902 /**
903 * The type evaluator to be used when calculating the animated values of this animation.
Chet Haaseb2ab04f2011-01-16 11:03:22 -0800904 * The system will automatically assign a float or int evaluator based on the type
Chet Haasea18a86b2010-09-07 13:20:00 -0700905 * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
906 * are not one of these primitive types, or if different evaluation is desired (such as is
907 * necessary with int values that represent colors), a custom evaluator needs to be assigned.
Chet Haase53ee3312011-01-10 15:56:56 -0800908 * For example, when running an animation on color values, the {@link ArgbEvaluator}
Chet Haasea18a86b2010-09-07 13:20:00 -0700909 * should be used to get correct RGB color interpolation.
910 *
911 * <p>If this ValueAnimator has only one set of values being animated between, this evaluator
912 * will be used for that set. If there are several sets of values being animated, which is
Chet Haasefdd3ad72013-04-24 16:38:20 -0700913 * the case if PropertyValuesHolder objects were set on the ValueAnimator, then the evaluator
Chet Haasea18a86b2010-09-07 13:20:00 -0700914 * is assigned just to the first PropertyValuesHolder object.</p>
915 *
916 * @param value the evaluator to be used this animation
917 */
918 public void setEvaluator(TypeEvaluator value) {
919 if (value != null && mValues != null && mValues.length > 0) {
920 mValues[0].setEvaluator(value);
921 }
922 }
923
Chet Haase17cf42c2012-04-17 13:18:14 -0700924 private void notifyStartListeners() {
925 if (mListeners != null && !mStartListenersCalled) {
926 ArrayList<AnimatorListener> tmpListeners =
927 (ArrayList<AnimatorListener>) mListeners.clone();
928 int numListeners = tmpListeners.size();
929 for (int i = 0; i < numListeners; ++i) {
930 tmpListeners.get(i).onAnimationStart(this);
931 }
932 }
933 mStartListenersCalled = true;
934 }
935
Chet Haasea18a86b2010-09-07 13:20:00 -0700936 /**
937 * Start the animation playing. This version of start() takes a boolean flag that indicates
938 * whether the animation should play in reverse. The flag is usually false, but may be set
Chet Haase2970c492010-11-09 13:58:04 -0800939 * to true if called from the reverse() method.
940 *
941 * <p>The animation started by calling this method will be run on the thread that called
942 * this method. This thread should have a Looper on it (a runtime exception will be thrown if
943 * this is not the case). Also, if the animation will animate
944 * properties of objects in the view hierarchy, then the calling thread should be the UI
945 * thread for that view hierarchy.</p>
Chet Haasea18a86b2010-09-07 13:20:00 -0700946 *
947 * @param playBackwards Whether the ValueAnimator should start playing in reverse.
948 */
949 private void start(boolean playBackwards) {
Chet Haase2970c492010-11-09 13:58:04 -0800950 if (Looper.myLooper() == null) {
951 throw new AndroidRuntimeException("Animators may only be run on Looper threads");
Jim Miller30604212010-09-22 19:56:23 -0700952 }
Chet Haasef4e3bab2014-12-02 17:51:34 -0800953 mReversing = playBackwards;
Doris Liufbe94ec2015-09-09 14:52:59 -0700954 // Special case: reversing from seek-to-0 should act as if not seeked at all.
955 if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
956 if (mRepeatCount == INFINITE) {
957 // Calculate the fraction of the current iteration.
958 float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
959 mSeekFraction = 1 - fraction;
Chet Haasef4e3bab2014-12-02 17:51:34 -0800960 } else {
Doris Liufbe94ec2015-09-09 14:52:59 -0700961 mSeekFraction = 1 + mRepeatCount - mSeekFraction;
Chet Haasef4e3bab2014-12-02 17:51:34 -0800962 }
963 }
Chet Haase8b699792011-08-05 15:20:19 -0700964 mStarted = true;
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700965 mPaused = false;
Doris Liu3618d302015-08-14 11:11:08 -0700966 mRunning = false;
Doris Liu3618d302015-08-14 11:11:08 -0700967 AnimationHandler animationHandler = AnimationHandler.getInstance();
Doris Liufbe94ec2015-09-09 14:52:59 -0700968 animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
Doris Liuf57bfe22015-10-01 13:26:01 -0700969
970 if (mStartDelay == 0) {
971 // If there's no start delay, init the animation and notify start listeners right away
Doris Liufbe94ec2015-09-09 14:52:59 -0700972 // to be consistent with the previous behavior. Otherwise, postpone this until the first
973 // frame after the start delay.
Doris Liuf57bfe22015-10-01 13:26:01 -0700974 startAnimation();
975 setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
976 }
Chet Haasea18a86b2010-09-07 13:20:00 -0700977 }
978
979 @Override
980 public void start() {
981 start(false);
982 }
983
984 @Override
985 public void cancel() {
Doris Liu3618d302015-08-14 11:11:08 -0700986 if (Looper.myLooper() == null) {
987 throw new AndroidRuntimeException("Animators may only be run on Looper threads");
988 }
Doris Liu3dbaae12015-08-27 14:56:30 -0700989
990 // If end has already been requested, through a previous end() or cancel() call, no-op
991 // until animation starts again.
992 if (mAnimationEndRequested) {
993 return;
994 }
995
Chet Haase2970c492010-11-09 13:58:04 -0800996 // Only cancel if the animation is actually running or has been started and is about
997 // to run
Doris Liu3618d302015-08-14 11:11:08 -0700998 // Only notify listeners if the animator has actually started
999 if ((mStarted || mRunning) && mListeners != null) {
1000 if (!mRunning) {
1001 // If it's not yet running, then start listeners weren't called. Call them now.
1002 notifyStartListeners();
Chet Haase7dfacdb2011-07-11 17:01:56 -07001003 }
Doris Liu3618d302015-08-14 11:11:08 -07001004 ArrayList<AnimatorListener> tmpListeners =
1005 (ArrayList<AnimatorListener>) mListeners.clone();
1006 for (AnimatorListener listener : tmpListeners) {
1007 listener.onAnimationCancel(this);
1008 }
Chet Haase2970c492010-11-09 13:58:04 -08001009 }
Doris Liu3618d302015-08-14 11:11:08 -07001010 endAnimation();
1011
Chet Haasea18a86b2010-09-07 13:20:00 -07001012 }
1013
1014 @Override
1015 public void end() {
Doris Liu3618d302015-08-14 11:11:08 -07001016 if (Looper.myLooper() == null) {
1017 throw new AndroidRuntimeException("Animators may only be run on Looper threads");
1018 }
1019 if (!mRunning) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001020 // Special case if the animation has not yet started; get it ready for ending
Doris Liu3618d302015-08-14 11:11:08 -07001021 startAnimation();
Chet Haase17cf42c2012-04-17 13:18:14 -07001022 mStarted = true;
Chet Haaseadd65772011-02-09 16:47:29 -08001023 } else if (!mInitialized) {
1024 initAnimation();
Chet Haasea18a86b2010-09-07 13:20:00 -07001025 }
Doris Liufbe94ec2015-09-09 14:52:59 -07001026 animateValue(shouldPlayBackward(mRepeatCount) ? 0f : 1f);
Doris Liu3618d302015-08-14 11:11:08 -07001027 endAnimation();
Chet Haasea18a86b2010-09-07 13:20:00 -07001028 }
1029
1030 @Override
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001031 public void resume() {
1032 if (mPaused) {
1033 mResumed = true;
1034 }
1035 super.resume();
1036 }
1037
1038 @Override
1039 public void pause() {
1040 boolean previouslyPaused = mPaused;
1041 super.pause();
1042 if (!previouslyPaused && mPaused) {
1043 mPauseTime = -1;
1044 mResumed = false;
1045 }
1046 }
1047
1048 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -07001049 public boolean isRunning() {
Doris Liu3618d302015-08-14 11:11:08 -07001050 return mRunning;
Chet Haase8b699792011-08-05 15:20:19 -07001051 }
1052
1053 @Override
1054 public boolean isStarted() {
1055 return mStarted;
Chet Haasea18a86b2010-09-07 13:20:00 -07001056 }
1057
1058 /**
1059 * Plays the ValueAnimator in reverse. If the animation is already running,
1060 * it will stop itself and play backwards from the point reached when reverse was called.
1061 * If the animation is not currently running, then it will start from the end and
1062 * play backwards. This behavior is only set for the current animation; future playing
1063 * of the animation will use the default behavior of playing forward.
1064 */
ztenghui7bc6a3f2014-07-15 15:12:12 -07001065 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -07001066 public void reverse() {
Doris Liu3618d302015-08-14 11:11:08 -07001067 if (mRunning) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001068 long currentTime = AnimationUtils.currentAnimationTimeMillis();
1069 long currentPlayTime = currentTime - mStartTime;
Doris Liufbe94ec2015-09-09 14:52:59 -07001070 long timeLeft = getScaledDuration() - currentPlayTime;
Chet Haasea18a86b2010-09-07 13:20:00 -07001071 mStartTime = currentTime - timeLeft;
Jeff Brownc42b28d2015-04-06 19:49:02 -07001072 mStartTimeCommitted = true; // do not allow start time to be compensated for jank
Chet Haasef4e3bab2014-12-02 17:51:34 -08001073 mReversing = !mReversing;
Chet Haasef43fb2a2013-09-06 07:59:36 -07001074 } else if (mStarted) {
1075 end();
Chet Haasea18a86b2010-09-07 13:20:00 -07001076 } else {
1077 start(true);
1078 }
1079 }
1080
1081 /**
ztenghui7bc6a3f2014-07-15 15:12:12 -07001082 * @hide
1083 */
1084 @Override
1085 public boolean canReverse() {
1086 return true;
1087 }
1088
1089 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001090 * Called internally to end an animation by removing it from the animations list. Must be
1091 * called on the UI thread.
1092 */
Doris Liu3dbaae12015-08-27 14:56:30 -07001093 private void endAnimation() {
1094 if (mAnimationEndRequested) {
1095 return;
1096 }
Doris Liu3618d302015-08-14 11:11:08 -07001097 AnimationHandler handler = AnimationHandler.getInstance();
1098 handler.removeCallback(this);
Doris Liu3dbaae12015-08-27 14:56:30 -07001099
1100 mAnimationEndRequested = true;
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001101 mPaused = false;
Chet Haase17cf42c2012-04-17 13:18:14 -07001102 if ((mStarted || mRunning) && mListeners != null) {
1103 if (!mRunning) {
1104 // If it's not yet running, then start listeners weren't called. Call them now.
1105 notifyStartListeners();
1106 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001107 ArrayList<AnimatorListener> tmpListeners =
1108 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -07001109 int numListeners = tmpListeners.size();
1110 for (int i = 0; i < numListeners; ++i) {
1111 tmpListeners.get(i).onAnimationEnd(this);
Chet Haasea18a86b2010-09-07 13:20:00 -07001112 }
1113 }
Chet Haase8b699792011-08-05 15:20:19 -07001114 mRunning = false;
Chet Haaseb8f574a2011-08-03 14:10:06 -07001115 mStarted = false;
Chet Haase17cf42c2012-04-17 13:18:14 -07001116 mStartListenersCalled = false;
Chet Haasef4e3bab2014-12-02 17:51:34 -08001117 mReversing = false;
Doris Liuf57bfe22015-10-01 13:26:01 -07001118 mLastFrameTime = 0;
Chet Haase9b80ca82013-06-04 09:37:38 -07001119 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
1120 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
1121 System.identityHashCode(this));
1122 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001123 }
1124
1125 /**
1126 * Called internally to start an animation by adding it to the active animations list. Must be
1127 * called on the UI thread.
1128 */
Doris Liu3618d302015-08-14 11:11:08 -07001129 private void startAnimation() {
Chet Haase9b80ca82013-06-04 09:37:38 -07001130 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
1131 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
1132 System.identityHashCode(this));
1133 }
Doris Liu66d48562015-11-09 14:44:35 -08001134
1135 mAnimationEndRequested = false;
Chet Haasea18a86b2010-09-07 13:20:00 -07001136 initAnimation();
Doris Liu3618d302015-08-14 11:11:08 -07001137 mRunning = true;
Doris Liufbe94ec2015-09-09 14:52:59 -07001138 if (mSeekFraction >= 0) {
1139 mOverallFraction = mSeekFraction;
1140 } else {
1141 mOverallFraction = 0f;
1142 }
Doris Liu3618d302015-08-14 11:11:08 -07001143 if (mListeners != null) {
Chet Haase17cf42c2012-04-17 13:18:14 -07001144 notifyStartListeners();
Chet Haasea18a86b2010-09-07 13:20:00 -07001145 }
1146 }
1147
1148 /**
Chet Haasefdd3ad72013-04-24 16:38:20 -07001149 * Returns the name of this animator for debugging purposes.
1150 */
1151 String getNameForTrace() {
1152 return "animator";
1153 }
1154
Chet Haasea18a86b2010-09-07 13:20:00 -07001155 /**
Jeff Brownc42b28d2015-04-06 19:49:02 -07001156 * Applies an adjustment to the animation to compensate for jank between when
1157 * the animation first ran and when the frame was drawn.
Doris Liu3618d302015-08-14 11:11:08 -07001158 * @hide
Jeff Brownc42b28d2015-04-06 19:49:02 -07001159 */
Doris Liu3618d302015-08-14 11:11:08 -07001160 public void commitAnimationFrame(long frameTime) {
Jeff Brownc42b28d2015-04-06 19:49:02 -07001161 if (!mStartTimeCommitted) {
1162 mStartTimeCommitted = true;
Doris Liu3618d302015-08-14 11:11:08 -07001163 long adjustment = frameTime - mLastFrameTime;
1164 if (adjustment > 0) {
Jeff Brownc42b28d2015-04-06 19:49:02 -07001165 mStartTime += adjustment;
1166 if (DEBUG) {
1167 Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString());
1168 }
1169 }
1170 }
1171 }
1172
1173 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001174 * This internal function processes a single animation frame for a given animation. The
1175 * currentTime parameter is the timing pulse sent by the handler, used to calculate the
1176 * elapsed duration, and therefore
1177 * the elapsed fraction, of the animation. The return value indicates whether the animation
1178 * should be ended (which happens when the elapsed time of the animation exceeds the
1179 * animation's duration, including the repeatCount).
1180 *
1181 * @param currentTime The current time, as tracked by the static timing handler
1182 * @return true if the animation's duration, including any repetitions due to
Doris Liu3618d302015-08-14 11:11:08 -07001183 * <code>repeatCount</code> has been exceeded and the animation should be ended.
Chet Haasea18a86b2010-09-07 13:20:00 -07001184 */
Doris Liu3618d302015-08-14 11:11:08 -07001185 boolean animateBasedOnTime(long currentTime) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001186 boolean done = false;
Doris Liu3618d302015-08-14 11:11:08 -07001187 if (mRunning) {
Doris Liufbe94ec2015-09-09 14:52:59 -07001188 final float fraction = getScaledDuration() > 0 ?
1189 (float)(currentTime - mStartTime) / getScaledDuration() : 1f;
1190 final float lastFraction = mOverallFraction;
1191 final boolean newIteration = (int) fraction > (int) lastFraction;
1192 final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
1193 (mRepeatCount != INFINITE);
1194 if (newIteration && !lastIterationFinished) {
1195 // Time to repeat
1196 if (mListeners != null) {
1197 int numListeners = mListeners.size();
1198 for (int i = 0; i < numListeners; ++i) {
1199 mListeners.get(i).onAnimationRepeat(this);
Chet Haasea18a86b2010-09-07 13:20:00 -07001200 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001201 }
Doris Liufbe94ec2015-09-09 14:52:59 -07001202 } else if (lastIterationFinished) {
1203 done = true;
Chet Haasea18a86b2010-09-07 13:20:00 -07001204 }
Doris Liufbe94ec2015-09-09 14:52:59 -07001205 mOverallFraction = clampFraction(fraction);
1206 float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
1207 animateValue(currentIterationFraction);
Chet Haasea18a86b2010-09-07 13:20:00 -07001208 }
Chet Haasea18a86b2010-09-07 13:20:00 -07001209 return done;
1210 }
1211
1212 /**
Jeff Brown20c4f872012-04-26 17:38:21 -07001213 * Processes a frame of the animation, adjusting the start time if needed.
1214 *
1215 * @param frameTime The frame time.
1216 * @return true if the animation has ended.
Doris Liu3618d302015-08-14 11:11:08 -07001217 * @hide
Jeff Brown20c4f872012-04-26 17:38:21 -07001218 */
Doris Liu3618d302015-08-14 11:11:08 -07001219 public final void doAnimationFrame(long frameTime) {
Doris Liu3618d302015-08-14 11:11:08 -07001220 AnimationHandler handler = AnimationHandler.getInstance();
Doris Liuf57bfe22015-10-01 13:26:01 -07001221 if (mLastFrameTime == 0) {
Doris Liu3618d302015-08-14 11:11:08 -07001222 // First frame
1223 handler.addOneShotCommitCallback(this);
Doris Liuf57bfe22015-10-01 13:26:01 -07001224 if (mStartDelay > 0) {
1225 startAnimation();
1226 }
Chet Haase0d1c27a2014-11-03 18:35:16 +00001227 if (mSeekFraction < 0) {
Jeff Brown20c4f872012-04-26 17:38:21 -07001228 mStartTime = frameTime;
1229 } else {
Doris Liufbe94ec2015-09-09 14:52:59 -07001230 long seekTime = (long) (getScaledDuration() * mSeekFraction);
Chet Haase0d1c27a2014-11-03 18:35:16 +00001231 mStartTime = frameTime - seekTime;
1232 mSeekFraction = -1;
Jeff Brown20c4f872012-04-26 17:38:21 -07001233 }
Jeff Brownc42b28d2015-04-06 19:49:02 -07001234 mStartTimeCommitted = false; // allow start time to be compensated for jank
Jeff Brown20c4f872012-04-26 17:38:21 -07001235 }
Doris Liuf57bfe22015-10-01 13:26:01 -07001236 mLastFrameTime = frameTime;
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001237 if (mPaused) {
1238 if (mPauseTime < 0) {
1239 mPauseTime = frameTime;
1240 }
Doris Liu3618d302015-08-14 11:11:08 -07001241 return;
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001242 } else if (mResumed) {
1243 mResumed = false;
1244 if (mPauseTime > 0) {
1245 // Offset by the duration that the animation was paused
1246 mStartTime += (frameTime - mPauseTime);
Jeff Brownc42b28d2015-04-06 19:49:02 -07001247 mStartTimeCommitted = false; // allow start time to be compensated for jank
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001248 }
Doris Liu3618d302015-08-14 11:11:08 -07001249 handler.addOneShotCommitCallback(this);
Chet Haase8aa1ffb2013-08-08 14:00:00 -07001250 }
Jeff Brown20c4f872012-04-26 17:38:21 -07001251 // The frame time might be before the start time during the first frame of
1252 // an animation. The "current time" must always be on or after the start
1253 // time to avoid animating frames at negative time intervals. In practice, this
1254 // is very rare and only happens when seeking backwards.
1255 final long currentTime = Math.max(frameTime, mStartTime);
Doris Liu3618d302015-08-14 11:11:08 -07001256 boolean finished = animateBasedOnTime(currentTime);
1257
1258 if (finished) {
1259 endAnimation();
1260 }
Jeff Brown20c4f872012-04-26 17:38:21 -07001261 }
1262
1263 /**
Chet Haasea00f3862011-02-22 06:34:40 -08001264 * Returns the current animation fraction, which is the elapsed/interpolated fraction used in
1265 * the most recent frame update on the animation.
1266 *
1267 * @return Elapsed/interpolated fraction of the animation.
1268 */
1269 public float getAnimatedFraction() {
1270 return mCurrentFraction;
1271 }
1272
1273 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001274 * This method is called with the elapsed fraction of the animation during every
1275 * animation frame. This function turns the elapsed fraction into an interpolated fraction
1276 * and then into an animated value (from the evaluator. The function is called mostly during
1277 * animation updates, but it is also called when the <code>end()</code>
1278 * function is called, to set the final value on the property.
1279 *
1280 * <p>Overrides of this method must call the superclass to perform the calculation
1281 * of the animated value.</p>
1282 *
1283 * @param fraction The elapsed fraction of the animation.
1284 */
Tor Norbyec615c6f2015-03-02 10:11:44 -08001285 @CallSuper
Chet Haasea18a86b2010-09-07 13:20:00 -07001286 void animateValue(float fraction) {
1287 fraction = mInterpolator.getInterpolation(fraction);
Chet Haasea00f3862011-02-22 06:34:40 -08001288 mCurrentFraction = fraction;
Chet Haasea18a86b2010-09-07 13:20:00 -07001289 int numValues = mValues.length;
1290 for (int i = 0; i < numValues; ++i) {
1291 mValues[i].calculateValue(fraction);
1292 }
1293 if (mUpdateListeners != null) {
1294 int numListeners = mUpdateListeners.size();
1295 for (int i = 0; i < numListeners; ++i) {
1296 mUpdateListeners.get(i).onAnimationUpdate(this);
1297 }
1298 }
1299 }
1300
1301 @Override
1302 public ValueAnimator clone() {
1303 final ValueAnimator anim = (ValueAnimator) super.clone();
1304 if (mUpdateListeners != null) {
Yigit Boyard422dc32014-09-25 12:23:35 -07001305 anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>(mUpdateListeners);
Chet Haasea18a86b2010-09-07 13:20:00 -07001306 }
Chet Haase0d1c27a2014-11-03 18:35:16 +00001307 anim.mSeekFraction = -1;
Chet Haasef4e3bab2014-12-02 17:51:34 -08001308 anim.mReversing = false;
Chet Haasea18a86b2010-09-07 13:20:00 -07001309 anim.mInitialized = false;
ztenghui26e9a192015-04-10 13:14:17 -07001310 anim.mStarted = false;
1311 anim.mRunning = false;
1312 anim.mPaused = false;
1313 anim.mResumed = false;
1314 anim.mStartListenersCalled = false;
ztenghuie1b5c2b2015-04-21 14:17:00 -07001315 anim.mStartTime = 0;
1316 anim.mStartTimeCommitted = false;
Doris Liu3dbaae12015-08-27 14:56:30 -07001317 anim.mAnimationEndRequested = false;
ztenghuie1b5c2b2015-04-21 14:17:00 -07001318 anim.mPauseTime = 0;
Doris Liu3618d302015-08-14 11:11:08 -07001319 anim.mLastFrameTime = 0;
Doris Liufbe94ec2015-09-09 14:52:59 -07001320 anim.mOverallFraction = 0;
ztenghuie1b5c2b2015-04-21 14:17:00 -07001321 anim.mCurrentFraction = 0;
ztenghui26e9a192015-04-10 13:14:17 -07001322
Chet Haasea18a86b2010-09-07 13:20:00 -07001323 PropertyValuesHolder[] oldValues = mValues;
1324 if (oldValues != null) {
1325 int numValues = oldValues.length;
1326 anim.mValues = new PropertyValuesHolder[numValues];
Chet Haasea18a86b2010-09-07 13:20:00 -07001327 anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
1328 for (int i = 0; i < numValues; ++i) {
Chet Haased4dd7022011-04-04 15:25:09 -07001329 PropertyValuesHolder newValuesHolder = oldValues[i].clone();
1330 anim.mValues[i] = newValuesHolder;
1331 anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder);
Chet Haasea18a86b2010-09-07 13:20:00 -07001332 }
1333 }
1334 return anim;
1335 }
1336
1337 /**
1338 * Implementors of this interface can add themselves as update listeners
1339 * to an <code>ValueAnimator</code> instance to receive callbacks on every animation
1340 * frame, after the current frame's values have been calculated for that
1341 * <code>ValueAnimator</code>.
1342 */
1343 public static interface AnimatorUpdateListener {
1344 /**
1345 * <p>Notifies the occurrence of another frame of the animation.</p>
1346 *
1347 * @param animation The animation which was repeated.
1348 */
1349 void onAnimationUpdate(ValueAnimator animation);
1350
1351 }
Brad Fitzpatrick599ca292010-10-22 14:47:03 -07001352
1353 /**
1354 * Return the number of animations currently running.
1355 *
Jeff Brown9c38dbe2011-12-02 16:22:46 -08001356 * Used by StrictMode internally to annotate violations.
1357 * May be called on arbitrary threads!
Brad Fitzpatrick599ca292010-10-22 14:47:03 -07001358 *
1359 * @hide
1360 */
1361 public static int getCurrentAnimationsCount() {
Doris Liu3618d302015-08-14 11:11:08 -07001362 return AnimationHandler.getAnimationCount();
Patrick Dubroy8901ffa2011-01-16 17:13:55 -08001363 }
Chet Haasee9140a72011-02-16 16:23:29 -08001364
1365 @Override
1366 public String toString() {
1367 String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode());
1368 if (mValues != null) {
1369 for (int i = 0; i < mValues.length; ++i) {
1370 returnVal += "\n " + mValues[i].toString();
1371 }
1372 }
1373 return returnVal;
1374 }
John Reckd3de42c2014-07-15 14:29:33 -07001375
1376 /**
1377 * <p>Whether or not the ValueAnimator is allowed to run asynchronously off of
1378 * the UI thread. This is a hint that informs the ValueAnimator that it is
1379 * OK to run the animation off-thread, however ValueAnimator may decide
1380 * that it must run the animation on the UI thread anyway. For example if there
1381 * is an {@link AnimatorUpdateListener} the animation will run on the UI thread,
1382 * regardless of the value of this hint.</p>
1383 *
1384 * <p>Regardless of whether or not the animation runs asynchronously, all
1385 * listener callbacks will be called on the UI thread.</p>
1386 *
1387 * <p>To be able to use this hint the following must be true:</p>
1388 * <ol>
1389 * <li>{@link #getAnimatedFraction()} is not needed (it will return undefined values).</li>
1390 * <li>The animator is immutable while {@link #isStarted()} is true. Requests
1391 * to change values, duration, delay, etc... may be ignored.</li>
1392 * <li>Lifecycle callback events may be asynchronous. Events such as
1393 * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or
1394 * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed
1395 * as they must be posted back to the UI thread, and any actions performed
1396 * by those callbacks (such as starting new animations) will not happen
1397 * in the same frame.</li>
1398 * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...)
1399 * may be asynchronous. It is guaranteed that all state changes that are
1400 * performed on the UI thread in the same frame will be applied as a single
1401 * atomic update, however that frame may be the current frame,
1402 * the next frame, or some future frame. This will also impact the observed
1403 * state of the Animator. For example, {@link #isStarted()} may still return true
1404 * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over
1405 * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()}
1406 * for this reason.</li>
1407 * </ol>
1408 * @hide
1409 */
John Reckc01bd112014-07-18 16:22:09 -07001410 @Override
John Reckd3de42c2014-07-15 14:29:33 -07001411 public void setAllowRunningAsynchronously(boolean mayRunAsync) {
1412 // It is up to subclasses to support this, if they can.
1413 }
Brad Fitzpatrick599ca292010-10-22 14:47:03 -07001414}