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