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 android.animation;
18 
19 import android.graphics.Path;
20 import android.graphics.PointF;
21 import android.util.FloatProperty;
22 import android.util.IntProperty;
23 import android.util.Log;
24 import android.util.Property;
25 
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.HashMap;
29 import java.util.List;
30 
31 /**
32  * This class holds information about a property and the values that that property
33  * should take on during an animation. PropertyValuesHolder objects can be used to create
34  * animations with ValueAnimator or ObjectAnimator that operate on several different properties
35  * in parallel.
36  */
37 public class PropertyValuesHolder implements Cloneable {
38 
39     /**
40      * The name of the property associated with the values. This need not be a real property,
41      * unless this object is being used with ObjectAnimator. But this is the name by which
42      * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
43      */
44     String mPropertyName;
45 
46     /**
47      * @hide
48      */
49     protected Property mProperty;
50 
51     /**
52      * The setter function, if needed. ObjectAnimator hands off this functionality to
53      * PropertyValuesHolder, since it holds all of the per-property information. This
54      * property is automatically
55      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
56      */
57     Method mSetter = null;
58 
59     /**
60      * The getter function, if needed. ObjectAnimator hands off this functionality to
61      * PropertyValuesHolder, since it holds all of the per-property information. This
62      * property is automatically
63      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
64      * The getter is only derived and used if one of the values is null.
65      */
66     private Method mGetter = null;
67 
68     /**
69      * The type of values supplied. This information is used both in deriving the setter/getter
70      * functions and in deriving the type of TypeEvaluator.
71      */
72     Class mValueType;
73 
74     /**
75      * The set of keyframes (time/value pairs) that define this animation.
76      */
77     Keyframes mKeyframes = null;
78 
79 
80     // type evaluators for the primitive types handled by this implementation
81     private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
82     private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
83 
84     // We try several different types when searching for appropriate setter/getter functions.
85     // The caller may have supplied values in a type that does not match the setter/getter
86     // functions (such as the integers 0 and 1 to represent floating point values for alpha).
87     // Also, the use of generics in constructors means that we end up with the Object versions
88     // of primitive types (Float vs. float). But most likely, the setter/getter functions
89     // will take primitive types instead.
90     // So we supply an ordered array of other types to try before giving up.
91     private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
92             Double.class, Integer.class};
93     private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
94             Float.class, Double.class};
95     private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
96             Float.class, Integer.class};
97 
98     // These maps hold all property entries for a particular class. This map
99     // is used to speed up property/setter/getter lookups for a given class/property
100     // combination. No need to use reflection on the combination more than once.
101     private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
102             new HashMap<Class, HashMap<String, Method>>();
103     private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
104             new HashMap<Class, HashMap<String, Method>>();
105 
106     // Used to pass single value to varargs parameter in setter invocation
107     final Object[] mTmpValueArray = new Object[1];
108 
109     /**
110      * The type evaluator used to calculate the animated values. This evaluator is determined
111      * automatically based on the type of the start/end objects passed into the constructor,
112      * but the system only knows about the primitive types int and float. Any other
113      * type will need to set the evaluator to a custom evaluator for that type.
114      */
115     private TypeEvaluator mEvaluator;
116 
117     /**
118      * The value most recently calculated by calculateValue(). This is set during
119      * that function and might be retrieved later either by ValueAnimator.animatedValue() or
120      * by the property-setting logic in ObjectAnimator.animatedValue().
121      */
122     private Object mAnimatedValue;
123 
124     /**
125      * Converts from the source Object type to the setter Object type.
126      */
127     private TypeConverter mConverter;
128 
129     /**
130      * Internal utility constructor, used by the factory methods to set the property name.
131      * @param propertyName The name of the property for this holder.
132      */
PropertyValuesHolder(String propertyName)133     private PropertyValuesHolder(String propertyName) {
134         mPropertyName = propertyName;
135     }
136 
137     /**
138      * Internal utility constructor, used by the factory methods to set the property.
139      * @param property The property for this holder.
140      */
PropertyValuesHolder(Property property)141     private PropertyValuesHolder(Property property) {
142         mProperty = property;
143         if (property != null) {
144             mPropertyName = property.getName();
145         }
146     }
147 
148     /**
149      * Constructs and returns a PropertyValuesHolder with a given property name and
150      * set of int values.
151      * @param propertyName The name of the property being animated.
152      * @param values The values that the named property will animate between.
153      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
154      */
ofInt(String propertyName, int... values)155     public static PropertyValuesHolder ofInt(String propertyName, int... values) {
156         return new IntPropertyValuesHolder(propertyName, values);
157     }
158 
159     /**
160      * Constructs and returns a PropertyValuesHolder with a given property and
161      * set of int values.
162      * @param property The property being animated. Should not be null.
163      * @param values The values that the property will animate between.
164      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
165      */
ofInt(Property<?, Integer> property, int... values)166     public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
167         return new IntPropertyValuesHolder(property, values);
168     }
169 
170     /**
171      * Constructs and returns a PropertyValuesHolder with a given property name and
172      * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied,
173      * a start and end value. If more values are supplied, the values will be animated from the
174      * start, through all intermediate values to the end value. When used with ObjectAnimator,
175      * the elements of the array represent the parameters of the setter function.
176      *
177      * @param propertyName The name of the property being animated. Can also be the
178      *                     case-sensitive name of the entire setter method. Should not be null.
179      * @param values The values that the property will animate between.
180      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
181      * @see IntArrayEvaluator#IntArrayEvaluator(int[])
182      * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
183      */
ofMultiInt(String propertyName, int[][] values)184     public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) {
185         if (values.length < 2) {
186             throw new IllegalArgumentException("At least 2 values must be supplied");
187         }
188         int numParameters = 0;
189         for (int i = 0; i < values.length; i++) {
190             if (values[i] == null) {
191                 throw new IllegalArgumentException("values must not be null");
192             }
193             int length = values[i].length;
194             if (i == 0) {
195                 numParameters = length;
196             } else if (length != numParameters) {
197                 throw new IllegalArgumentException("Values must all have the same length");
198             }
199         }
200         IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]);
201         return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values);
202     }
203 
204     /**
205      * Constructs and returns a PropertyValuesHolder with a given property name to use
206      * as a multi-int setter. The values are animated along the path, with the first
207      * parameter of the setter set to the x coordinate and the second set to the y coordinate.
208      *
209      * @param propertyName The name of the property being animated. Can also be the
210      *                     case-sensitive name of the entire setter method. Should not be null.
211      *                     The setter must take exactly two <code>int</code> parameters.
212      * @param path The Path along which the values should be animated.
213      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
214      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
215      */
ofMultiInt(String propertyName, Path path)216     public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) {
217         Keyframes keyframes = KeyframeSet.ofPath(path);
218         PointFToIntArray converter = new PointFToIntArray();
219         return new MultiIntValuesHolder(propertyName, converter, null, keyframes);
220     }
221 
222     /**
223      * Constructs and returns a PropertyValuesHolder with a given property and
224      * set of Object values for use with ObjectAnimator multi-value setters. The Object
225      * values are converted to <code>int[]</code> using the converter.
226      *
227      * @param propertyName The property being animated or complete name of the setter.
228      *                     Should not be null.
229      * @param converter Used to convert the animated value to setter parameters.
230      * @param evaluator A TypeEvaluator that will be called on each animation frame to
231      * provide the necessary interpolation between the Object values to derive the animated
232      * value.
233      * @param values The values that the property will animate between.
234      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
235      * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
236      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
237      */
ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)238     public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
239             TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
240         return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
241     }
242 
243     /**
244      * Constructs and returns a PropertyValuesHolder object with the specified property name or
245      * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
246      * of any type, but the type should be consistent so that the supplied
247      * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
248      * <code>converter</code> converts the values to parameters in the setter function.
249      *
250      * <p>At least two values must be supplied, a start and an end value.</p>
251      *
252      * @param propertyName The name of the property to associate with the set of values. This
253      *                     may also be the complete name of a setter function.
254      * @param converter    Converts <code>values</code> into int parameters for the setter.
255      *                     Can be null if the Keyframes have int[] values.
256      * @param evaluator    Used to interpolate between values.
257      * @param values       The values at specific fractional times to evaluate between
258      * @return A PropertyValuesHolder for a multi-int parameter setter.
259      */
ofMultiInt(String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)260     public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
261             TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
262         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
263         return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
264     }
265 
266     /**
267      * Constructs and returns a PropertyValuesHolder with a given property name and
268      * set of float values.
269      * @param propertyName The name of the property being animated.
270      * @param values The values that the named property will animate between.
271      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
272      */
ofFloat(String propertyName, float... values)273     public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
274         return new FloatPropertyValuesHolder(propertyName, values);
275     }
276 
277     /**
278      * Constructs and returns a PropertyValuesHolder with a given property and
279      * set of float values.
280      * @param property The property being animated. Should not be null.
281      * @param values The values that the property will animate between.
282      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
283      */
ofFloat(Property<?, Float> property, float... values)284     public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
285         return new FloatPropertyValuesHolder(property, values);
286     }
287 
288     /**
289      * Constructs and returns a PropertyValuesHolder with a given property name and
290      * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
291      * a start and end value. If more values are supplied, the values will be animated from the
292      * start, through all intermediate values to the end value. When used with ObjectAnimator,
293      * the elements of the array represent the parameters of the setter function.
294      *
295      * @param propertyName The name of the property being animated. Can also be the
296      *                     case-sensitive name of the entire setter method. Should not be null.
297      * @param values The values that the property will animate between.
298      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
299      * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
300      * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
301      */
ofMultiFloat(String propertyName, float[][] values)302     public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
303         if (values.length < 2) {
304             throw new IllegalArgumentException("At least 2 values must be supplied");
305         }
306         int numParameters = 0;
307         for (int i = 0; i < values.length; i++) {
308             if (values[i] == null) {
309                 throw new IllegalArgumentException("values must not be null");
310             }
311             int length = values[i].length;
312             if (i == 0) {
313                 numParameters = length;
314             } else if (length != numParameters) {
315                 throw new IllegalArgumentException("Values must all have the same length");
316             }
317         }
318         FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
319         return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
320     }
321 
322     /**
323      * Constructs and returns a PropertyValuesHolder with a given property name to use
324      * as a multi-float setter. The values are animated along the path, with the first
325      * parameter of the setter set to the x coordinate and the second set to the y coordinate.
326      *
327      * @param propertyName The name of the property being animated. Can also be the
328      *                     case-sensitive name of the entire setter method. Should not be null.
329      *                     The setter must take exactly two <code>float</code> parameters.
330      * @param path The Path along which the values should be animated.
331      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
332      * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
333      */
ofMultiFloat(String propertyName, Path path)334     public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
335         Keyframes keyframes = KeyframeSet.ofPath(path);
336         PointFToFloatArray converter = new PointFToFloatArray();
337         return new MultiFloatValuesHolder(propertyName, converter, null, keyframes);
338     }
339 
340     /**
341      * Constructs and returns a PropertyValuesHolder with a given property and
342      * set of Object values for use with ObjectAnimator multi-value setters. The Object
343      * values are converted to <code>float[]</code> using the converter.
344      *
345      * @param propertyName The property being animated or complete name of the setter.
346      *                     Should not be null.
347      * @param converter Used to convert the animated value to setter parameters.
348      * @param evaluator A TypeEvaluator that will be called on each animation frame to
349      * provide the necessary interpolation between the Object values to derive the animated
350      * value.
351      * @param values The values that the property will animate between.
352      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
353      * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
354      */
ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)355     public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
356             TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
357         return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
358     }
359 
360     /**
361      * Constructs and returns a PropertyValuesHolder object with the specified property name or
362      * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
363      * of any type, but the type should be consistent so that the supplied
364      * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
365      * <code>converter</code> converts the values to parameters in the setter function.
366      *
367      * <p>At least two values must be supplied, a start and an end value.</p>
368      *
369      * @param propertyName The name of the property to associate with the set of values. This
370      *                     may also be the complete name of a setter function.
371      * @param converter    Converts <code>values</code> into float parameters for the setter.
372      *                     Can be null if the Keyframes have float[] values.
373      * @param evaluator    Used to interpolate between values.
374      * @param values       The values at specific fractional times to evaluate between
375      * @return A PropertyValuesHolder for a multi-float parameter setter.
376      */
ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)377     public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
378             TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
379         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
380         return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
381     }
382 
383     /**
384      * Constructs and returns a PropertyValuesHolder with a given property name and
385      * set of Object values. This variant also takes a TypeEvaluator because the system
386      * cannot automatically interpolate between objects of unknown type.
387      *
388      * @param propertyName The name of the property being animated.
389      * @param evaluator A TypeEvaluator that will be called on each animation frame to
390      * provide the necessary interpolation between the Object values to derive the animated
391      * value.
392      * @param values The values that the named property will animate between.
393      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
394      */
ofObject(String propertyName, TypeEvaluator evaluator, Object... values)395     public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
396             Object... values) {
397         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
398         pvh.setObjectValues(values);
399         pvh.setEvaluator(evaluator);
400         return pvh;
401     }
402 
403     /**
404      * Constructs and returns a PropertyValuesHolder with a given property name and
405      * a Path along which the values should be animated. This variant supports a
406      * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
407      * type.
408      *
409      * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
410      * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
411      * not be stored by the setter or TypeConverter.</p>
412      *
413      * @param propertyName The name of the property being animated.
414      * @param converter Converts a PointF to the type associated with the setter. May be
415      *                  null if conversion is unnecessary.
416      * @param path The Path along which the values should be animated.
417      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
418      */
ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)419     public static PropertyValuesHolder ofObject(String propertyName,
420             TypeConverter<PointF, ?> converter, Path path) {
421         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
422         pvh.mKeyframes = KeyframeSet.ofPath(path);
423         pvh.mValueType = PointF.class;
424         pvh.setConverter(converter);
425         return pvh;
426     }
427 
428     /**
429      * Constructs and returns a PropertyValuesHolder with a given property and
430      * set of Object values. This variant also takes a TypeEvaluator because the system
431      * cannot automatically interpolate between objects of unknown type.
432      *
433      * @param property The property being animated. Should not be null.
434      * @param evaluator A TypeEvaluator that will be called on each animation frame to
435      * provide the necessary interpolation between the Object values to derive the animated
436      * value.
437      * @param values The values that the property will animate between.
438      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
439      */
ofObject(Property property, TypeEvaluator<V> evaluator, V... values)440     public static <V> PropertyValuesHolder ofObject(Property property,
441             TypeEvaluator<V> evaluator, V... values) {
442         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
443         pvh.setObjectValues(values);
444         pvh.setEvaluator(evaluator);
445         return pvh;
446     }
447 
448     /**
449      * Constructs and returns a PropertyValuesHolder with a given property and
450      * set of Object values. This variant also takes a TypeEvaluator because the system
451      * cannot automatically interpolate between objects of unknown type. This variant also
452      * takes a <code>TypeConverter</code> to convert from animated values to the type
453      * of the property. If only one value is supplied, the <code>TypeConverter</code>
454      * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
455      * value.
456      *
457      * @param property The property being animated. Should not be null.
458      * @param converter Converts the animated object to the Property type.
459      * @param evaluator A TypeEvaluator that will be called on each animation frame to
460      * provide the necessary interpolation between the Object values to derive the animated
461      * value.
462      * @param values The values that the property will animate between.
463      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
464      * @see #setConverter(TypeConverter)
465      * @see TypeConverter
466      */
ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)467     public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
468             TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
469         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
470         pvh.setConverter(converter);
471         pvh.setObjectValues(values);
472         pvh.setEvaluator(evaluator);
473         return pvh;
474     }
475 
476     /**
477      * Constructs and returns a PropertyValuesHolder with a given property and
478      * a Path along which the values should be animated. This variant supports a
479      * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
480      * type.
481      *
482      * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
483      * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
484      * not be stored by the setter or TypeConverter.</p>
485      *
486      * @param property The property being animated. Should not be null.
487      * @param converter Converts a PointF to the type associated with the setter. May be
488      *                  null if conversion is unnecessary.
489      * @param path The Path along which the values should be animated.
490      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
491      */
ofObject(Property<?, V> property, TypeConverter<PointF, V> converter, Path path)492     public static <V> PropertyValuesHolder ofObject(Property<?, V> property,
493             TypeConverter<PointF, V> converter, Path path) {
494         PropertyValuesHolder pvh = new PropertyValuesHolder(property);
495         pvh.mKeyframes = KeyframeSet.ofPath(path);
496         pvh.mValueType = PointF.class;
497         pvh.setConverter(converter);
498         return pvh;
499     }
500 
501     /**
502      * Constructs and returns a PropertyValuesHolder object with the specified property name and set
503      * of values. These values can be of any type, but the type should be consistent so that
504      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
505      * the common type.
506      * <p>If there is only one value, it is assumed to be the end value of an animation,
507      * and an initial value will be derived, if possible, by calling a getter function
508      * on the object. Also, if any value is null, the value will be filled in when the animation
509      * starts in the same way. This mechanism of automatically getting null values only works
510      * if the PropertyValuesHolder object is used in conjunction
511      * {@link ObjectAnimator}, and with a getter function
512      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
513      * no way of determining what the value should be.
514      * @param propertyName The name of the property associated with this set of values. This
515      * can be the actual property name to be used when using a ObjectAnimator object, or
516      * just a name used to get animated values, such as if this object is used with an
517      * ValueAnimator object.
518      * @param values The set of values to animate between.
519      */
ofKeyframe(String propertyName, Keyframe... values)520     public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
521         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
522         return ofKeyframes(propertyName, keyframeSet);
523     }
524 
525     /**
526      * Constructs and returns a PropertyValuesHolder object with the specified property and set
527      * of values. These values can be of any type, but the type should be consistent so that
528      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
529      * the common type.
530      * <p>If there is only one value, it is assumed to be the end value of an animation,
531      * and an initial value will be derived, if possible, by calling the property's
532      * {@link android.util.Property#get(Object)} function.
533      * Also, if any value is null, the value will be filled in when the animation
534      * starts in the same way. This mechanism of automatically getting null values only works
535      * if the PropertyValuesHolder object is used in conjunction with
536      * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
537      * no way of determining what the value should be.
538      * @param property The property associated with this set of values. Should not be null.
539      * @param values The set of values to animate between.
540      */
ofKeyframe(Property property, Keyframe... values)541     public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
542         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
543         return ofKeyframes(property, keyframeSet);
544     }
545 
ofKeyframes(String propertyName, Keyframes keyframes)546     static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) {
547         if (keyframes instanceof Keyframes.IntKeyframes) {
548             return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes);
549         } else if (keyframes instanceof Keyframes.FloatKeyframes) {
550             return new FloatPropertyValuesHolder(propertyName,
551                     (Keyframes.FloatKeyframes) keyframes);
552         } else {
553             PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
554             pvh.mKeyframes = keyframes;
555             pvh.mValueType = keyframes.getType();
556             return pvh;
557         }
558     }
559 
ofKeyframes(Property property, Keyframes keyframes)560     static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) {
561         if (keyframes instanceof Keyframes.IntKeyframes) {
562             return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes);
563         } else if (keyframes instanceof Keyframes.FloatKeyframes) {
564             return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes);
565         } else {
566             PropertyValuesHolder pvh = new PropertyValuesHolder(property);
567             pvh.mKeyframes = keyframes;
568             pvh.mValueType = keyframes.getType();
569             return pvh;
570         }
571     }
572 
573     /**
574      * Set the animated values for this object to this set of ints.
575      * If there is only one value, it is assumed to be the end value of an animation,
576      * and an initial value will be derived, if possible, by calling a getter function
577      * on the object. Also, if any value is null, the value will be filled in when the animation
578      * starts in the same way. This mechanism of automatically getting null values only works
579      * if the PropertyValuesHolder object is used in conjunction
580      * {@link ObjectAnimator}, and with a getter function
581      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
582      * no way of determining what the value should be.
583      *
584      * @param values One or more values that the animation will animate between.
585      */
setIntValues(int... values)586     public void setIntValues(int... values) {
587         mValueType = int.class;
588         mKeyframes = KeyframeSet.ofInt(values);
589     }
590 
591     /**
592      * Set the animated values for this object to this set of floats.
593      * If there is only one value, it is assumed to be the end value of an animation,
594      * and an initial value will be derived, if possible, by calling a getter function
595      * on the object. Also, if any value is null, the value will be filled in when the animation
596      * starts in the same way. This mechanism of automatically getting null values only works
597      * if the PropertyValuesHolder object is used in conjunction
598      * {@link ObjectAnimator}, and with a getter function
599      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
600      * no way of determining what the value should be.
601      *
602      * @param values One or more values that the animation will animate between.
603      */
setFloatValues(float... values)604     public void setFloatValues(float... values) {
605         mValueType = float.class;
606         mKeyframes = KeyframeSet.ofFloat(values);
607     }
608 
609     /**
610      * Set the animated values for this object to this set of Keyframes.
611      *
612      * @param values One or more values that the animation will animate between.
613      */
setKeyframes(Keyframe... values)614     public void setKeyframes(Keyframe... values) {
615         int numKeyframes = values.length;
616         Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
617         mValueType = ((Keyframe)values[0]).getType();
618         for (int i = 0; i < numKeyframes; ++i) {
619             keyframes[i] = (Keyframe)values[i];
620         }
621         mKeyframes = new KeyframeSet(keyframes);
622     }
623 
624     /**
625      * Set the animated values for this object to this set of Objects.
626      * If there is only one value, it is assumed to be the end value of an animation,
627      * and an initial value will be derived, if possible, by calling a getter function
628      * on the object. Also, if any value is null, the value will be filled in when the animation
629      * starts in the same way. This mechanism of automatically getting null values only works
630      * if the PropertyValuesHolder object is used in conjunction
631      * {@link ObjectAnimator}, and with a getter function
632      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
633      * no way of determining what the value should be.
634      *
635      * @param values One or more values that the animation will animate between.
636      */
setObjectValues(Object... values)637     public void setObjectValues(Object... values) {
638         mValueType = values[0].getClass();
639         mKeyframes = KeyframeSet.ofObject(values);
640         if (mEvaluator != null) {
641             mKeyframes.setEvaluator(mEvaluator);
642         }
643     }
644 
645     /**
646      * Sets the converter to convert from the values type to the setter's parameter type.
647      * If only one value is supplied, <var>converter</var> must be a
648      * {@link android.animation.BidirectionalTypeConverter}.
649      * @param converter The converter to use to convert values.
650      */
setConverter(TypeConverter converter)651     public void setConverter(TypeConverter converter) {
652         mConverter = converter;
653     }
654 
655     /**
656      * Determine the setter or getter function using the JavaBeans convention of setFoo or
657      * getFoo for a property named 'foo'. This function figures out what the name of the
658      * function should be and uses reflection to find the Method with that name on the
659      * target object.
660      *
661      * @param targetClass The class to search for the method
662      * @param prefix "set" or "get", depending on whether we need a setter or getter.
663      * @param valueType The type of the parameter (in the case of a setter). This type
664      * is derived from the values set on this PropertyValuesHolder. This type is used as
665      * a first guess at the parameter type, but we check for methods with several different
666      * types to avoid problems with slight mis-matches between supplied values and actual
667      * value types used on the setter.
668      * @return Method the method associated with mPropertyName.
669      */
getPropertyFunction(Class targetClass, String prefix, Class valueType)670     private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
671         // TODO: faster implementation...
672         Method returnVal = null;
673         String methodName = getMethodName(prefix, mPropertyName);
674         Class args[] = null;
675         if (valueType == null) {
676             try {
677                 returnVal = targetClass.getMethod(methodName, args);
678             } catch (NoSuchMethodException e) {
679                 // Swallow the error, log it later
680             }
681         } else {
682             args = new Class[1];
683             Class typeVariants[];
684             if (valueType.equals(Float.class)) {
685                 typeVariants = FLOAT_VARIANTS;
686             } else if (valueType.equals(Integer.class)) {
687                 typeVariants = INTEGER_VARIANTS;
688             } else if (valueType.equals(Double.class)) {
689                 typeVariants = DOUBLE_VARIANTS;
690             } else {
691                 typeVariants = new Class[1];
692                 typeVariants[0] = valueType;
693             }
694             for (Class typeVariant : typeVariants) {
695                 args[0] = typeVariant;
696                 try {
697                     returnVal = targetClass.getMethod(methodName, args);
698                     if (mConverter == null) {
699                         // change the value type to suit
700                         mValueType = typeVariant;
701                     }
702                     return returnVal;
703                 } catch (NoSuchMethodException e) {
704                     // Swallow the error and keep trying other variants
705                 }
706             }
707             // If we got here, then no appropriate function was found
708         }
709 
710         if (returnVal == null) {
711             Log.w("PropertyValuesHolder", "Method " +
712                     getMethodName(prefix, mPropertyName) + "() with type " + valueType +
713                     " not found on target class " + targetClass);
714         }
715 
716         return returnVal;
717     }
718 
719 
720     /**
721      * Returns the setter or getter requested. This utility function checks whether the
722      * requested method exists in the propertyMapMap cache. If not, it calls another
723      * utility function to request the Method from the targetClass directly.
724      * @param targetClass The Class on which the requested method should exist.
725      * @param propertyMapMap The cache of setters/getters derived so far.
726      * @param prefix "set" or "get", for the setter or getter.
727      * @param valueType The type of parameter passed into the method (null for getter).
728      * @return Method the method associated with mPropertyName.
729      */
setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType)730     private Method setupSetterOrGetter(Class targetClass,
731             HashMap<Class, HashMap<String, Method>> propertyMapMap,
732             String prefix, Class valueType) {
733         Method setterOrGetter = null;
734         synchronized(propertyMapMap) {
735             // Have to lock property map prior to reading it, to guard against
736             // another thread putting something in there after we've checked it
737             // but before we've added an entry to it
738             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
739             boolean wasInMap = false;
740             if (propertyMap != null) {
741                 wasInMap = propertyMap.containsKey(mPropertyName);
742                 if (wasInMap) {
743                     setterOrGetter = propertyMap.get(mPropertyName);
744                 }
745             }
746             if (!wasInMap) {
747                 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
748                 if (propertyMap == null) {
749                     propertyMap = new HashMap<String, Method>();
750                     propertyMapMap.put(targetClass, propertyMap);
751                 }
752                 propertyMap.put(mPropertyName, setterOrGetter);
753             }
754         }
755         return setterOrGetter;
756     }
757 
758     /**
759      * Utility function to get the setter from targetClass
760      * @param targetClass The Class on which the requested method should exist.
761      */
setupSetter(Class targetClass)762     void setupSetter(Class targetClass) {
763         Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
764         mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
765     }
766 
767     /**
768      * Utility function to get the getter from targetClass
769      */
setupGetter(Class targetClass)770     private void setupGetter(Class targetClass) {
771         mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
772     }
773 
774     /**
775      * Internal function (called from ObjectAnimator) to set up the setter and getter
776      * prior to running the animation. If the setter has not been manually set for this
777      * object, it will be derived automatically given the property name, target object, and
778      * types of values supplied. If no getter has been set, it will be supplied iff any of the
779      * supplied values was null. If there is a null value, then the getter (supplied or derived)
780      * will be called to set those null values to the current value of the property
781      * on the target object.
782      * @param target The object on which the setter (and possibly getter) exist.
783      */
setupSetterAndGetter(Object target)784     void setupSetterAndGetter(Object target) {
785         mKeyframes.invalidateCache();
786         if (mProperty != null) {
787             // check to make sure that mProperty is on the class of target
788             try {
789                 Object testValue = null;
790                 List<Keyframe> keyframes = mKeyframes.getKeyframes();
791                 int keyframeCount = keyframes == null ? 0 : keyframes.size();
792                 for (int i = 0; i < keyframeCount; i++) {
793                     Keyframe kf = keyframes.get(i);
794                     if (!kf.hasValue() || kf.valueWasSetOnStart()) {
795                         if (testValue == null) {
796                             testValue = convertBack(mProperty.get(target));
797                         }
798                         kf.setValue(testValue);
799                         kf.setValueWasSetOnStart(true);
800                     }
801                 }
802                 return;
803             } catch (ClassCastException e) {
804                 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
805                         ") on target object " + target + ". Trying reflection instead");
806                 mProperty = null;
807             }
808         }
809         // We can't just say 'else' here because the catch statement sets mProperty to null.
810         if (mProperty == null) {
811             Class targetClass = target.getClass();
812             if (mSetter == null) {
813                 setupSetter(targetClass);
814             }
815             List<Keyframe> keyframes = mKeyframes.getKeyframes();
816             int keyframeCount = keyframes == null ? 0 : keyframes.size();
817             for (int i = 0; i < keyframeCount; i++) {
818                 Keyframe kf = keyframes.get(i);
819                 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
820                     if (mGetter == null) {
821                         setupGetter(targetClass);
822                         if (mGetter == null) {
823                             // Already logged the error - just return to avoid NPE
824                             return;
825                         }
826                     }
827                     try {
828                         Object value = convertBack(mGetter.invoke(target));
829                         kf.setValue(value);
830                         kf.setValueWasSetOnStart(true);
831                     } catch (InvocationTargetException e) {
832                         Log.e("PropertyValuesHolder", e.toString());
833                     } catch (IllegalAccessException e) {
834                         Log.e("PropertyValuesHolder", e.toString());
835                     }
836                 }
837             }
838         }
839     }
840 
convertBack(Object value)841     private Object convertBack(Object value) {
842         if (mConverter != null) {
843             if (!(mConverter instanceof BidirectionalTypeConverter)) {
844                 throw new IllegalArgumentException("Converter "
845                         + mConverter.getClass().getName()
846                         + " must be a BidirectionalTypeConverter");
847             }
848             value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
849         }
850         return value;
851     }
852 
853     /**
854      * Utility function to set the value stored in a particular Keyframe. The value used is
855      * whatever the value is for the property name specified in the keyframe on the target object.
856      *
857      * @param target The target object from which the current value should be extracted.
858      * @param kf The keyframe which holds the property name and value.
859      */
setupValue(Object target, Keyframe kf)860     private void setupValue(Object target, Keyframe kf) {
861         if (mProperty != null) {
862             Object value = convertBack(mProperty.get(target));
863             kf.setValue(value);
864         }
865         try {
866             if (mGetter == null) {
867                 Class targetClass = target.getClass();
868                 setupGetter(targetClass);
869                 if (mGetter == null) {
870                     // Already logged the error - just return to avoid NPE
871                     return;
872                 }
873             }
874             Object value = convertBack(mGetter.invoke(target));
875             kf.setValue(value);
876         } catch (InvocationTargetException e) {
877             Log.e("PropertyValuesHolder", e.toString());
878         } catch (IllegalAccessException e) {
879             Log.e("PropertyValuesHolder", e.toString());
880         }
881     }
882 
883     /**
884      * This function is called by ObjectAnimator when setting the start values for an animation.
885      * The start values are set according to the current values in the target object. The
886      * property whose value is extracted is whatever is specified by the propertyName of this
887      * PropertyValuesHolder object.
888      *
889      * @param target The object which holds the start values that should be set.
890      */
setupStartValue(Object target)891     void setupStartValue(Object target) {
892         List<Keyframe> keyframes = mKeyframes.getKeyframes();
893         if (!keyframes.isEmpty()) {
894             setupValue(target, keyframes.get(0));
895         }
896     }
897 
898     /**
899      * This function is called by ObjectAnimator when setting the end values for an animation.
900      * The end values are set according to the current values in the target object. The
901      * property whose value is extracted is whatever is specified by the propertyName of this
902      * PropertyValuesHolder object.
903      *
904      * @param target The object which holds the start values that should be set.
905      */
setupEndValue(Object target)906     void setupEndValue(Object target) {
907         List<Keyframe> keyframes = mKeyframes.getKeyframes();
908         if (!keyframes.isEmpty()) {
909             setupValue(target, keyframes.get(keyframes.size() - 1));
910         }
911     }
912 
913     @Override
clone()914     public PropertyValuesHolder clone() {
915         try {
916             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
917             newPVH.mPropertyName = mPropertyName;
918             newPVH.mProperty = mProperty;
919             newPVH.mKeyframes = mKeyframes.clone();
920             newPVH.mEvaluator = mEvaluator;
921             return newPVH;
922         } catch (CloneNotSupportedException e) {
923             // won't reach here
924             return null;
925         }
926     }
927 
928     /**
929      * Internal function to set the value on the target object, using the setter set up
930      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
931      * to handle turning the value calculated by ValueAnimator into a value set on the object
932      * according to the name of the property.
933      * @param target The target object on which the value is set
934      */
setAnimatedValue(Object target)935     void setAnimatedValue(Object target) {
936         if (mProperty != null) {
937             mProperty.set(target, getAnimatedValue());
938         }
939         if (mSetter != null) {
940             try {
941                 mTmpValueArray[0] = getAnimatedValue();
942                 mSetter.invoke(target, mTmpValueArray);
943             } catch (InvocationTargetException e) {
944                 Log.e("PropertyValuesHolder", e.toString());
945             } catch (IllegalAccessException e) {
946                 Log.e("PropertyValuesHolder", e.toString());
947             }
948         }
949     }
950 
951     /**
952      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
953      * to calculate animated values.
954      */
init()955     void init() {
956         if (mEvaluator == null) {
957             // We already handle int and float automatically, but not their Object
958             // equivalents
959             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
960                     (mValueType == Float.class) ? sFloatEvaluator :
961                     null;
962         }
963         if (mEvaluator != null) {
964             // KeyframeSet knows how to evaluate the common types - only give it a custom
965             // evaluator if one has been set on this class
966             mKeyframes.setEvaluator(mEvaluator);
967         }
968     }
969 
970     /**
971      * The TypeEvaluator will be automatically determined based on the type of values
972      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
973      * desired. This may be important in cases where either the type of the values supplied
974      * do not match the way that they should be interpolated between, or if the values
975      * are of a custom type or one not currently understood by the animation system. Currently,
976      * only values of type float and int (and their Object equivalents: Float
977      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
978      * @param evaluator
979      */
setEvaluator(TypeEvaluator evaluator)980     public void setEvaluator(TypeEvaluator evaluator) {
981         mEvaluator = evaluator;
982         mKeyframes.setEvaluator(evaluator);
983     }
984 
985     /**
986      * Function used to calculate the value according to the evaluator set up for
987      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
988      *
989      * @param fraction The elapsed, interpolated fraction of the animation.
990      */
calculateValue(float fraction)991     void calculateValue(float fraction) {
992         Object value = mKeyframes.getValue(fraction);
993         mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
994     }
995 
996     /**
997      * Sets the name of the property that will be animated. This name is used to derive
998      * a setter function that will be called to set animated values.
999      * For example, a property name of <code>foo</code> will result
1000      * in a call to the function <code>setFoo()</code> on the target object. If either
1001      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1002      * also be derived and called.
1003      *
1004      * <p>Note that the setter function derived from this property name
1005      * must take the same parameter type as the
1006      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
1007      * the setter function will fail.</p>
1008      *
1009      * @param propertyName The name of the property being animated.
1010      */
setPropertyName(String propertyName)1011     public void setPropertyName(String propertyName) {
1012         mPropertyName = propertyName;
1013     }
1014 
1015     /**
1016      * Sets the property that will be animated.
1017      *
1018      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
1019      * must exist on the target object specified in that ObjectAnimator.</p>
1020      *
1021      * @param property The property being animated.
1022      */
setProperty(Property property)1023     public void setProperty(Property property) {
1024         mProperty = property;
1025     }
1026 
1027     /**
1028      * Gets the name of the property that will be animated. This name will be used to derive
1029      * a setter function that will be called to set animated values.
1030      * For example, a property name of <code>foo</code> will result
1031      * in a call to the function <code>setFoo()</code> on the target object. If either
1032      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1033      * also be derived and called.
1034      */
getPropertyName()1035     public String getPropertyName() {
1036         return mPropertyName;
1037     }
1038 
1039     /**
1040      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
1041      * most recently calculated in calculateValue().
1042      * @return
1043      */
getAnimatedValue()1044     Object getAnimatedValue() {
1045         return mAnimatedValue;
1046     }
1047 
1048     @Override
toString()1049     public String toString() {
1050         return mPropertyName + ": " + mKeyframes.toString();
1051     }
1052 
1053     /**
1054      * Utility method to derive a setter/getter method name from a property name, where the
1055      * prefix is typically "set" or "get" and the first letter of the property name is
1056      * capitalized.
1057      *
1058      * @param prefix The precursor to the method name, before the property name begins, typically
1059      * "set" or "get".
1060      * @param propertyName The name of the property that represents the bulk of the method name
1061      * after the prefix. The first letter of this word will be capitalized in the resulting
1062      * method name.
1063      * @return String the property name converted to a method name according to the conventions
1064      * specified above.
1065      */
getMethodName(String prefix, String propertyName)1066     static String getMethodName(String prefix, String propertyName) {
1067         if (propertyName == null || propertyName.length() == 0) {
1068             // shouldn't get here
1069             return prefix;
1070         }
1071         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
1072         String theRest = propertyName.substring(1);
1073         return prefix + firstLetter + theRest;
1074     }
1075 
1076     static class IntPropertyValuesHolder extends PropertyValuesHolder {
1077 
1078         // Cache JNI functions to avoid looking them up twice
1079         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1080                 new HashMap<Class, HashMap<String, Long>>();
1081         long mJniSetter;
1082         private IntProperty mIntProperty;
1083 
1084         Keyframes.IntKeyframes mIntKeyframes;
1085         int mIntAnimatedValue;
1086 
IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes)1087         public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
1088             super(propertyName);
1089             mValueType = int.class;
1090             mKeyframes = keyframes;
1091             mIntKeyframes = keyframes;
1092         }
1093 
IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes)1094         public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
1095             super(property);
1096             mValueType = int.class;
1097             mKeyframes = keyframes;
1098             mIntKeyframes = keyframes;
1099             if (property instanceof  IntProperty) {
1100                 mIntProperty = (IntProperty) mProperty;
1101             }
1102         }
1103 
IntPropertyValuesHolder(String propertyName, int... values)1104         public IntPropertyValuesHolder(String propertyName, int... values) {
1105             super(propertyName);
1106             setIntValues(values);
1107         }
1108 
IntPropertyValuesHolder(Property property, int... values)1109         public IntPropertyValuesHolder(Property property, int... values) {
1110             super(property);
1111             setIntValues(values);
1112             if (property instanceof  IntProperty) {
1113                 mIntProperty = (IntProperty) mProperty;
1114             }
1115         }
1116 
1117         @Override
setIntValues(int... values)1118         public void setIntValues(int... values) {
1119             super.setIntValues(values);
1120             mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
1121         }
1122 
1123         @Override
calculateValue(float fraction)1124         void calculateValue(float fraction) {
1125             mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
1126         }
1127 
1128         @Override
getAnimatedValue()1129         Object getAnimatedValue() {
1130             return mIntAnimatedValue;
1131         }
1132 
1133         @Override
clone()1134         public IntPropertyValuesHolder clone() {
1135             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
1136             newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
1137             return newPVH;
1138         }
1139 
1140         /**
1141          * Internal function to set the value on the target object, using the setter set up
1142          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1143          * to handle turning the value calculated by ValueAnimator into a value set on the object
1144          * according to the name of the property.
1145          * @param target The target object on which the value is set
1146          */
1147         @Override
setAnimatedValue(Object target)1148         void setAnimatedValue(Object target) {
1149             if (mIntProperty != null) {
1150                 mIntProperty.setValue(target, mIntAnimatedValue);
1151                 return;
1152             }
1153             if (mProperty != null) {
1154                 mProperty.set(target, mIntAnimatedValue);
1155                 return;
1156             }
1157             if (mJniSetter != 0) {
1158                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1159                 return;
1160             }
1161             if (mSetter != null) {
1162                 try {
1163                     mTmpValueArray[0] = mIntAnimatedValue;
1164                     mSetter.invoke(target, mTmpValueArray);
1165                 } catch (InvocationTargetException e) {
1166                     Log.e("PropertyValuesHolder", e.toString());
1167                 } catch (IllegalAccessException e) {
1168                     Log.e("PropertyValuesHolder", e.toString());
1169                 }
1170             }
1171         }
1172 
1173         @Override
setupSetter(Class targetClass)1174         void setupSetter(Class targetClass) {
1175             if (mProperty != null) {
1176                 return;
1177             }
1178             // Check new static hashmap<propName, int> for setter method
1179             synchronized(sJNISetterPropertyMap) {
1180                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1181                 boolean wasInMap = false;
1182                 if (propertyMap != null) {
1183                     wasInMap = propertyMap.containsKey(mPropertyName);
1184                     if (wasInMap) {
1185                         Long jniSetter = propertyMap.get(mPropertyName);
1186                         if (jniSetter != null) {
1187                             mJniSetter = jniSetter;
1188                         }
1189                     }
1190                 }
1191                 if (!wasInMap) {
1192                     String methodName = getMethodName("set", mPropertyName);
1193                     try {
1194                         mJniSetter = nGetIntMethod(targetClass, methodName);
1195                     } catch (NoSuchMethodError e) {
1196                         // Couldn't find it via JNI - try reflection next. Probably means the method
1197                         // doesn't exist, or the type is wrong. An error will be logged later if
1198                         // reflection fails as well.
1199                     }
1200                     if (propertyMap == null) {
1201                         propertyMap = new HashMap<String, Long>();
1202                         sJNISetterPropertyMap.put(targetClass, propertyMap);
1203                     }
1204                     propertyMap.put(mPropertyName, mJniSetter);
1205                 }
1206             }
1207             if (mJniSetter == 0) {
1208                 // Couldn't find method through fast JNI approach - just use reflection
1209                 super.setupSetter(targetClass);
1210             }
1211         }
1212     }
1213 
1214     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1215 
1216         // Cache JNI functions to avoid looking them up twice
1217         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1218                 new HashMap<Class, HashMap<String, Long>>();
1219         long mJniSetter;
1220         private FloatProperty mFloatProperty;
1221 
1222         Keyframes.FloatKeyframes mFloatKeyframes;
1223         float mFloatAnimatedValue;
1224 
FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes)1225         public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
1226             super(propertyName);
1227             mValueType = float.class;
1228             mKeyframes = keyframes;
1229             mFloatKeyframes = keyframes;
1230         }
1231 
FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes)1232         public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
1233             super(property);
1234             mValueType = float.class;
1235             mKeyframes = keyframes;
1236             mFloatKeyframes = keyframes;
1237             if (property instanceof FloatProperty) {
1238                 mFloatProperty = (FloatProperty) mProperty;
1239             }
1240         }
1241 
FloatPropertyValuesHolder(String propertyName, float... values)1242         public FloatPropertyValuesHolder(String propertyName, float... values) {
1243             super(propertyName);
1244             setFloatValues(values);
1245         }
1246 
FloatPropertyValuesHolder(Property property, float... values)1247         public FloatPropertyValuesHolder(Property property, float... values) {
1248             super(property);
1249             setFloatValues(values);
1250             if (property instanceof  FloatProperty) {
1251                 mFloatProperty = (FloatProperty) mProperty;
1252             }
1253         }
1254 
1255         @Override
setFloatValues(float... values)1256         public void setFloatValues(float... values) {
1257             super.setFloatValues(values);
1258             mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
1259         }
1260 
1261         @Override
calculateValue(float fraction)1262         void calculateValue(float fraction) {
1263             mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
1264         }
1265 
1266         @Override
getAnimatedValue()1267         Object getAnimatedValue() {
1268             return mFloatAnimatedValue;
1269         }
1270 
1271         @Override
clone()1272         public FloatPropertyValuesHolder clone() {
1273             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1274             newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
1275             return newPVH;
1276         }
1277 
1278         /**
1279          * Internal function to set the value on the target object, using the setter set up
1280          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1281          * to handle turning the value calculated by ValueAnimator into a value set on the object
1282          * according to the name of the property.
1283          * @param target The target object on which the value is set
1284          */
1285         @Override
setAnimatedValue(Object target)1286         void setAnimatedValue(Object target) {
1287             if (mFloatProperty != null) {
1288                 mFloatProperty.setValue(target, mFloatAnimatedValue);
1289                 return;
1290             }
1291             if (mProperty != null) {
1292                 mProperty.set(target, mFloatAnimatedValue);
1293                 return;
1294             }
1295             if (mJniSetter != 0) {
1296                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1297                 return;
1298             }
1299             if (mSetter != null) {
1300                 try {
1301                     mTmpValueArray[0] = mFloatAnimatedValue;
1302                     mSetter.invoke(target, mTmpValueArray);
1303                 } catch (InvocationTargetException e) {
1304                     Log.e("PropertyValuesHolder", e.toString());
1305                 } catch (IllegalAccessException e) {
1306                     Log.e("PropertyValuesHolder", e.toString());
1307                 }
1308             }
1309         }
1310 
1311         @Override
setupSetter(Class targetClass)1312         void setupSetter(Class targetClass) {
1313             if (mProperty != null) {
1314                 return;
1315             }
1316             // Check new static hashmap<propName, int> for setter method
1317             synchronized (sJNISetterPropertyMap) {
1318                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1319                 boolean wasInMap = false;
1320                 if (propertyMap != null) {
1321                     wasInMap = propertyMap.containsKey(mPropertyName);
1322                     if (wasInMap) {
1323                         Long jniSetter = propertyMap.get(mPropertyName);
1324                         if (jniSetter != null) {
1325                             mJniSetter = jniSetter;
1326                         }
1327                     }
1328                 }
1329                 if (!wasInMap) {
1330                     String methodName = getMethodName("set", mPropertyName);
1331                     try {
1332                         mJniSetter = nGetFloatMethod(targetClass, methodName);
1333                     } catch (NoSuchMethodError e) {
1334                         // Couldn't find it via JNI - try reflection next. Probably means the method
1335                         // doesn't exist, or the type is wrong. An error will be logged later if
1336                         // reflection fails as well.
1337                     }
1338                     if (propertyMap == null) {
1339                         propertyMap = new HashMap<String, Long>();
1340                         sJNISetterPropertyMap.put(targetClass, propertyMap);
1341                     }
1342                     propertyMap.put(mPropertyName, mJniSetter);
1343                 }
1344             }
1345             if (mJniSetter == 0) {
1346                 // Couldn't find method through fast JNI approach - just use reflection
1347                 super.setupSetter(targetClass);
1348             }
1349         }
1350 
1351     }
1352 
1353     static class MultiFloatValuesHolder extends PropertyValuesHolder {
1354         private long mJniSetter;
1355         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1356                 new HashMap<Class, HashMap<String, Long>>();
1357 
MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1358         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1359                 TypeEvaluator evaluator, Object... values) {
1360             super(propertyName);
1361             setConverter(converter);
1362             setObjectValues(values);
1363             setEvaluator(evaluator);
1364         }
1365 
MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1366         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1367                 TypeEvaluator evaluator, Keyframes keyframes) {
1368             super(propertyName);
1369             setConverter(converter);
1370             mKeyframes = keyframes;
1371             setEvaluator(evaluator);
1372         }
1373 
1374         /**
1375          * Internal function to set the value on the target object, using the setter set up
1376          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1377          * to handle turning the value calculated by ValueAnimator into a value set on the object
1378          * according to the name of the property.
1379          *
1380          * @param target The target object on which the value is set
1381          */
1382         @Override
setAnimatedValue(Object target)1383         void setAnimatedValue(Object target) {
1384             float[] values = (float[]) getAnimatedValue();
1385             int numParameters = values.length;
1386             if (mJniSetter != 0) {
1387                 switch (numParameters) {
1388                     case 1:
1389                         nCallFloatMethod(target, mJniSetter, values[0]);
1390                         break;
1391                     case 2:
1392                         nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1393                         break;
1394                     case 4:
1395                         nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1396                                 values[2], values[3]);
1397                         break;
1398                     default: {
1399                         nCallMultipleFloatMethod(target, mJniSetter, values);
1400                         break;
1401                     }
1402                 }
1403             }
1404         }
1405 
1406         /**
1407          * Internal function (called from ObjectAnimator) to set up the setter and getter
1408          * prior to running the animation. No getter can be used for multiple parameters.
1409          *
1410          * @param target The object on which the setter exists.
1411          */
1412         @Override
setupSetterAndGetter(Object target)1413         void setupSetterAndGetter(Object target) {
1414             setupSetter(target.getClass());
1415         }
1416 
1417         @Override
setupSetter(Class targetClass)1418         void setupSetter(Class targetClass) {
1419             if (mJniSetter != 0) {
1420                 return;
1421             }
1422             synchronized(sJNISetterPropertyMap) {
1423                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1424                 boolean wasInMap = false;
1425                 if (propertyMap != null) {
1426                     wasInMap = propertyMap.containsKey(mPropertyName);
1427                     if (wasInMap) {
1428                         Long jniSetter = propertyMap.get(mPropertyName);
1429                         if (jniSetter != null) {
1430                             mJniSetter = jniSetter;
1431                         }
1432                     }
1433                 }
1434                 if (!wasInMap) {
1435                     String methodName = getMethodName("set", mPropertyName);
1436                     calculateValue(0f);
1437                     float[] values = (float[]) getAnimatedValue();
1438                     int numParams = values.length;
1439                     try {
1440                         mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1441                     } catch (NoSuchMethodError e) {
1442                         // try without the 'set' prefix
1443                         try {
1444                             mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
1445                                     numParams);
1446                         } catch (NoSuchMethodError e2) {
1447                             // just try reflection next
1448                         }
1449                     }
1450                     if (propertyMap == null) {
1451                         propertyMap = new HashMap<String, Long>();
1452                         sJNISetterPropertyMap.put(targetClass, propertyMap);
1453                     }
1454                     propertyMap.put(mPropertyName, mJniSetter);
1455                 }
1456            }
1457         }
1458     }
1459 
1460     static class MultiIntValuesHolder extends PropertyValuesHolder {
1461         private long mJniSetter;
1462         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1463                 new HashMap<Class, HashMap<String, Long>>();
1464 
MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)1465         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1466                 TypeEvaluator evaluator, Object... values) {
1467             super(propertyName);
1468             setConverter(converter);
1469             setObjectValues(values);
1470             setEvaluator(evaluator);
1471         }
1472 
MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)1473         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1474                 TypeEvaluator evaluator, Keyframes keyframes) {
1475             super(propertyName);
1476             setConverter(converter);
1477             mKeyframes = keyframes;
1478             setEvaluator(evaluator);
1479         }
1480 
1481         /**
1482          * Internal function to set the value on the target object, using the setter set up
1483          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1484          * to handle turning the value calculated by ValueAnimator into a value set on the object
1485          * according to the name of the property.
1486          *
1487          * @param target The target object on which the value is set
1488          */
1489         @Override
setAnimatedValue(Object target)1490         void setAnimatedValue(Object target) {
1491             int[] values = (int[]) getAnimatedValue();
1492             int numParameters = values.length;
1493             if (mJniSetter != 0) {
1494                 switch (numParameters) {
1495                     case 1:
1496                         nCallIntMethod(target, mJniSetter, values[0]);
1497                         break;
1498                     case 2:
1499                         nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1500                         break;
1501                     case 4:
1502                         nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1503                                 values[2], values[3]);
1504                         break;
1505                     default: {
1506                         nCallMultipleIntMethod(target, mJniSetter, values);
1507                         break;
1508                     }
1509                 }
1510             }
1511         }
1512 
1513         /**
1514          * Internal function (called from ObjectAnimator) to set up the setter and getter
1515          * prior to running the animation. No getter can be used for multiple parameters.
1516          *
1517          * @param target The object on which the setter exists.
1518          */
1519         @Override
setupSetterAndGetter(Object target)1520         void setupSetterAndGetter(Object target) {
1521             setupSetter(target.getClass());
1522         }
1523 
1524         @Override
setupSetter(Class targetClass)1525         void setupSetter(Class targetClass) {
1526             if (mJniSetter != 0) {
1527                 return;
1528             }
1529             synchronized(sJNISetterPropertyMap) {
1530                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1531                 boolean wasInMap = false;
1532                 if (propertyMap != null) {
1533                     wasInMap = propertyMap.containsKey(mPropertyName);
1534                     if (wasInMap) {
1535                         Long jniSetter = propertyMap.get(mPropertyName);
1536                         if (jniSetter != null) {
1537                             mJniSetter = jniSetter;
1538                         }
1539                     }
1540                 }
1541                 if (!wasInMap) {
1542                     String methodName = getMethodName("set", mPropertyName);
1543                     calculateValue(0f);
1544                     int[] values = (int[]) getAnimatedValue();
1545                     int numParams = values.length;
1546                     try {
1547                         mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1548                     } catch (NoSuchMethodError e) {
1549                         // try without the 'set' prefix
1550                         try {
1551                             mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
1552                                     numParams);
1553                         } catch (NoSuchMethodError e2) {
1554                             // couldn't find it.
1555                         }
1556                     }
1557                     if (propertyMap == null) {
1558                         propertyMap = new HashMap<String, Long>();
1559                         sJNISetterPropertyMap.put(targetClass, propertyMap);
1560                     }
1561                     propertyMap.put(mPropertyName, mJniSetter);
1562                 }
1563             }
1564         }
1565     }
1566 
1567     /**
1568      * Convert from PointF to float[] for multi-float setters along a Path.
1569      */
1570     private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1571         private float[] mCoordinates = new float[2];
1572 
PointFToFloatArray()1573         public PointFToFloatArray() {
1574             super(PointF.class, float[].class);
1575         }
1576 
1577         @Override
convert(PointF value)1578         public float[] convert(PointF value) {
1579             mCoordinates[0] = value.x;
1580             mCoordinates[1] = value.y;
1581             return mCoordinates;
1582         }
1583     };
1584 
1585     /**
1586      * Convert from PointF to int[] for multi-int setters along a Path.
1587      */
1588     private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1589         private int[] mCoordinates = new int[2];
1590 
PointFToIntArray()1591         public PointFToIntArray() {
1592             super(PointF.class, int[].class);
1593         }
1594 
1595         @Override
convert(PointF value)1596         public int[] convert(PointF value) {
1597             mCoordinates[0] = Math.round(value.x);
1598             mCoordinates[1] = Math.round(value.y);
1599             return mCoordinates;
1600         }
1601     };
1602 
nGetIntMethod(Class targetClass, String methodName)1603     native static private long nGetIntMethod(Class targetClass, String methodName);
nGetFloatMethod(Class targetClass, String methodName)1604     native static private long nGetFloatMethod(Class targetClass, String methodName);
nGetMultipleIntMethod(Class targetClass, String methodName, int numParams)1605     native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
1606             int numParams);
nGetMultipleFloatMethod(Class targetClass, String methodName, int numParams)1607     native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
1608             int numParams);
nCallIntMethod(Object target, long methodID, int arg)1609     native static private void nCallIntMethod(Object target, long methodID, int arg);
nCallFloatMethod(Object target, long methodID, float arg)1610     native static private void nCallFloatMethod(Object target, long methodID, float arg);
nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)1611     native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)1612     native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
1613             int arg3, int arg4);
nCallMultipleIntMethod(Object target, long methodID, int[] args)1614     native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)1615     native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
1616             float arg2);
nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)1617     native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
1618             float arg2, float arg3, float arg4);
nCallMultipleFloatMethod(Object target, long methodID, float[] args)1619     native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
1620 }
1621