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    // Used to pass single value to varargs parameter in setter invocation
109    final Object[] mTmpValueArray = new Object[1];
110
111    /**
112     * The type evaluator used to calculate the animated values. This evaluator is determined
113     * automatically based on the type of the start/end objects passed into the constructor,
114     * but the system only knows about the primitive types int and float. Any other
115     * type will need to set the evaluator to a custom evaluator for that type.
116     */
117    private TypeEvaluator mEvaluator;
118
119    /**
120     * The value most recently calculated by calculateValue(). This is set during
121     * that function and might be retrieved later either by ValueAnimator.animatedValue() or
122     * by the property-setting logic in ObjectAnimator.animatedValue().
123     */
124    private Object mAnimatedValue;
125
126    /**
127     * Converts from the source Object type to the setter Object type.
128     */
129    private TypeConverter mConverter;
130
131    /**
132     * Internal utility constructor, used by the factory methods to set the property name.
133     * @param propertyName The name of the property for this holder.
134     */
135    private PropertyValuesHolder(String propertyName) {
136        mPropertyName = propertyName;
137    }
138
139    /**
140     * Internal utility constructor, used by the factory methods to set the property.
141     * @param property The property for this holder.
142     */
143    private PropertyValuesHolder(Property property) {
144        mProperty = property;
145        if (property != null) {
146            mPropertyName = property.getName();
147        }
148    }
149
150    /**
151     * Constructs and returns a PropertyValuesHolder with a given property name and
152     * set of int values.
153     * @param propertyName The name of the property being animated.
154     * @param values The values that the named property will animate between.
155     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
156     */
157    public static PropertyValuesHolder ofInt(String propertyName, int... values) {
158        return new IntPropertyValuesHolder(propertyName, values);
159    }
160
161    /**
162     * Constructs and returns a PropertyValuesHolder with a given property and
163     * set of int values.
164     * @param property The property being animated. Should not be null.
165     * @param values The values that the property will animate between.
166     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
167     */
168    public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
169        return new IntPropertyValuesHolder(property, values);
170    }
171
172    /**
173     * Constructs and returns a PropertyValuesHolder with a given property name and
174     * set of <code>int[]</code> values. At least two <code>int[]</code> values must be supplied,
175     * a start and end value. If more values are supplied, the values will be animated from the
176     * start, through all intermediate values to the end value. When used with ObjectAnimator,
177     * the elements of the array represent the parameters of the setter function.
178     *
179     * @param propertyName The name of the property being animated. Can also be the
180     *                     case-sensitive name of the entire setter method. Should not be null.
181     * @param values The values that the property will animate between.
182     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
183     * @see IntArrayEvaluator#IntArrayEvaluator(int[])
184     * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
185     */
186    public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) {
187        if (values.length < 2) {
188            throw new IllegalArgumentException("At least 2 values must be supplied");
189        }
190        int numParameters = 0;
191        for (int i = 0; i < values.length; i++) {
192            if (values[i] == null) {
193                throw new IllegalArgumentException("values must not be null");
194            }
195            int length = values[i].length;
196            if (i == 0) {
197                numParameters = length;
198            } else if (length != numParameters) {
199                throw new IllegalArgumentException("Values must all have the same length");
200            }
201        }
202        IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]);
203        return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values);
204    }
205
206    /**
207     * Constructs and returns a PropertyValuesHolder with a given property name to use
208     * as a multi-int setter. The values are animated along the path, with the first
209     * parameter of the setter set to the x coordinate and the second set to the y coordinate.
210     *
211     * @param propertyName The name of the property being animated. Can also be the
212     *                     case-sensitive name of the entire setter method. Should not be null.
213     *                     The setter must take exactly two <code>int</code> parameters.
214     * @param path The Path along which the values should be animated.
215     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
216     * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
217     */
218    public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) {
219        Keyframes keyframes = KeyframeSet.ofPath(path);
220        PointFToIntArray converter = new PointFToIntArray();
221        return new MultiIntValuesHolder(propertyName, converter, null, keyframes);
222    }
223
224    /**
225     * Constructs and returns a PropertyValuesHolder with a given property and
226     * set of Object values for use with ObjectAnimator multi-value setters. The Object
227     * values are converted to <code>int[]</code> using the converter.
228     *
229     * @param propertyName The property being animated or complete name of the setter.
230     *                     Should not be null.
231     * @param converter Used to convert the animated value to setter parameters.
232     * @param evaluator A TypeEvaluator that will be called on each animation frame to
233     * provide the necessary interpolation between the Object values to derive the animated
234     * value.
235     * @param values The values that the property will animate between.
236     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
237     * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[])
238     * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
239     */
240    public static <V> PropertyValuesHolder ofMultiInt(String propertyName,
241            TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values) {
242        return new MultiIntValuesHolder(propertyName, converter, evaluator, values);
243    }
244
245    /**
246     * Constructs and returns a PropertyValuesHolder object with the specified property name or
247     * setter name for use in a multi-int setter function using ObjectAnimator. The values can be
248     * of any type, but the type should be consistent so that the supplied
249     * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
250     * <code>converter</code> converts the values to parameters in the setter function.
251     *
252     * <p>At least two values must be supplied, a start and an end value.</p>
253     *
254     * @param propertyName The name of the property to associate with the set of values. This
255     *                     may also be the complete name of a setter function.
256     * @param converter    Converts <code>values</code> into int parameters for the setter.
257     *                     Can be null if the Keyframes have int[] values.
258     * @param evaluator    Used to interpolate between values.
259     * @param values       The values at specific fractional times to evaluate between
260     * @return A PropertyValuesHolder for a multi-int parameter setter.
261     */
262    public static <T> PropertyValuesHolder ofMultiInt(String propertyName,
263            TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
264        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
265        return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet);
266    }
267
268    /**
269     * Constructs and returns a PropertyValuesHolder with a given property name and
270     * set of float values.
271     * @param propertyName The name of the property being animated.
272     * @param values The values that the named property will animate between.
273     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
274     */
275    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
276        return new FloatPropertyValuesHolder(propertyName, values);
277    }
278
279    /**
280     * Constructs and returns a PropertyValuesHolder with a given property and
281     * set of float values.
282     * @param property The property being animated. Should not be null.
283     * @param values The values that the property will animate between.
284     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
285     */
286    public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
287        return new FloatPropertyValuesHolder(property, values);
288    }
289
290    /**
291     * Constructs and returns a PropertyValuesHolder with a given property name and
292     * set of <code>float[]</code> values. At least two <code>float[]</code> values must be supplied,
293     * a start and end value. If more values are supplied, the values will be animated from the
294     * start, through all intermediate values to the end value. When used with ObjectAnimator,
295     * the elements of the array represent the parameters of the setter function.
296     *
297     * @param propertyName The name of the property being animated. Can also be the
298     *                     case-sensitive name of the entire setter method. Should not be null.
299     * @param values The values that the property will animate between.
300     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
301     * @see FloatArrayEvaluator#FloatArrayEvaluator(float[])
302     * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
303     */
304    public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) {
305        if (values.length < 2) {
306            throw new IllegalArgumentException("At least 2 values must be supplied");
307        }
308        int numParameters = 0;
309        for (int i = 0; i < values.length; i++) {
310            if (values[i] == null) {
311                throw new IllegalArgumentException("values must not be null");
312            }
313            int length = values[i].length;
314            if (i == 0) {
315                numParameters = length;
316            } else if (length != numParameters) {
317                throw new IllegalArgumentException("Values must all have the same length");
318            }
319        }
320        FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]);
321        return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values);
322    }
323
324    /**
325     * Constructs and returns a PropertyValuesHolder with a given property name to use
326     * as a multi-float setter. The values are animated along the path, with the first
327     * parameter of the setter set to the x coordinate and the second set to the y coordinate.
328     *
329     * @param propertyName The name of the property being animated. Can also be the
330     *                     case-sensitive name of the entire setter method. Should not be null.
331     *                     The setter must take exactly two <code>float</code> parameters.
332     * @param path The Path along which the values should be animated.
333     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
334     * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...)
335     */
336    public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) {
337        Keyframes keyframes = KeyframeSet.ofPath(path);
338        PointFToFloatArray converter = new PointFToFloatArray();
339        return new MultiFloatValuesHolder(propertyName, converter, null, keyframes);
340    }
341
342    /**
343     * Constructs and returns a PropertyValuesHolder with a given property and
344     * set of Object values for use with ObjectAnimator multi-value setters. The Object
345     * values are converted to <code>float[]</code> using the converter.
346     *
347     * @param propertyName The property being animated or complete name of the setter.
348     *                     Should not be null.
349     * @param converter Used to convert the animated value to setter parameters.
350     * @param evaluator A TypeEvaluator that will be called on each animation frame to
351     * provide the necessary interpolation between the Object values to derive the animated
352     * value.
353     * @param values The values that the property will animate between.
354     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
355     * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[])
356     */
357    public static <V> PropertyValuesHolder ofMultiFloat(String propertyName,
358            TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values) {
359        return new MultiFloatValuesHolder(propertyName, converter, evaluator, values);
360    }
361
362    /**
363     * Constructs and returns a PropertyValuesHolder object with the specified property name or
364     * setter name for use in a multi-float setter function using ObjectAnimator. The values can be
365     * of any type, but the type should be consistent so that the supplied
366     * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The
367     * <code>converter</code> converts the values to parameters in the setter function.
368     *
369     * <p>At least two values must be supplied, a start and an end value.</p>
370     *
371     * @param propertyName The name of the property to associate with the set of values. This
372     *                     may also be the complete name of a setter function.
373     * @param converter    Converts <code>values</code> into float parameters for the setter.
374     *                     Can be null if the Keyframes have float[] values.
375     * @param evaluator    Used to interpolate between values.
376     * @param values       The values at specific fractional times to evaluate between
377     * @return A PropertyValuesHolder for a multi-float parameter setter.
378     */
379    public static <T> PropertyValuesHolder ofMultiFloat(String propertyName,
380            TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values) {
381        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
382        return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet);
383    }
384
385    /**
386     * Constructs and returns a PropertyValuesHolder with a given property name and
387     * set of Object values. This variant also takes a TypeEvaluator because the system
388     * cannot automatically interpolate between objects of unknown type.
389     *
390     * @param propertyName The name of the property being animated.
391     * @param evaluator A TypeEvaluator that will be called on each animation frame to
392     * provide the necessary interpolation between the Object values to derive the animated
393     * value.
394     * @param values The values that the named property will animate between.
395     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
396     */
397    public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
398            Object... values) {
399        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
400        pvh.setObjectValues(values);
401        pvh.setEvaluator(evaluator);
402        return pvh;
403    }
404
405    /**
406     * Constructs and returns a PropertyValuesHolder with a given property name and
407     * a Path along which the values should be animated. This variant supports a
408     * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
409     * type.
410     *
411     * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
412     * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
413     * not be stored by the setter or TypeConverter.</p>
414     *
415     * @param propertyName The name of the property being animated.
416     * @param converter Converts a PointF to the type associated with the setter. May be
417     *                  null if conversion is unnecessary.
418     * @param path The Path along which the values should be animated.
419     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
420     */
421    public static PropertyValuesHolder ofObject(String propertyName,
422            TypeConverter<PointF, ?> converter, Path path) {
423        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
424        pvh.mKeyframes = KeyframeSet.ofPath(path);
425        pvh.mValueType = PointF.class;
426        pvh.setConverter(converter);
427        return pvh;
428    }
429
430    /**
431     * Constructs and returns a PropertyValuesHolder with a given property and
432     * set of Object values. This variant also takes a TypeEvaluator because the system
433     * cannot automatically interpolate between objects of unknown type.
434     *
435     * @param property The property being animated. Should not be null.
436     * @param evaluator A TypeEvaluator that will be called on each animation frame to
437     * provide the necessary interpolation between the Object values to derive the animated
438     * value.
439     * @param values The values that the property will animate between.
440     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
441     */
442    public static <V> PropertyValuesHolder ofObject(Property property,
443            TypeEvaluator<V> evaluator, V... values) {
444        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
445        pvh.setObjectValues(values);
446        pvh.setEvaluator(evaluator);
447        return pvh;
448    }
449
450    /**
451     * Constructs and returns a PropertyValuesHolder with a given property and
452     * set of Object values. This variant also takes a TypeEvaluator because the system
453     * cannot automatically interpolate between objects of unknown type. This variant also
454     * takes a <code>TypeConverter</code> to convert from animated values to the type
455     * of the property. If only one value is supplied, the <code>TypeConverter</code>
456     * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
457     * value.
458     *
459     * @param property The property being animated. Should not be null.
460     * @param converter Converts the animated object to the Property type.
461     * @param evaluator A TypeEvaluator that will be called on each animation frame to
462     * provide the necessary interpolation between the Object values to derive the animated
463     * value.
464     * @param values The values that the property will animate between.
465     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
466     * @see #setConverter(TypeConverter)
467     * @see TypeConverter
468     */
469    public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
470            TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
471        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
472        pvh.setConverter(converter);
473        pvh.setObjectValues(values);
474        pvh.setEvaluator(evaluator);
475        return pvh;
476    }
477
478    /**
479     * Constructs and returns a PropertyValuesHolder with a given property and
480     * a Path along which the values should be animated. This variant supports a
481     * <code>TypeConverter</code> to convert from <code>PointF</code> to the target
482     * type.
483     *
484     * <p>The PointF passed to <code>converter</code> or <code>property</code>, if
485     * <code>converter</code> is <code>null</code>, is reused on each animation frame and should
486     * not be stored by the setter or TypeConverter.</p>
487     *
488     * @param property The property being animated. Should not be null.
489     * @param converter Converts a PointF to the type associated with the setter. May be
490     *                  null if conversion is unnecessary.
491     * @param path The Path along which the values should be animated.
492     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
493     */
494    public static <V> PropertyValuesHolder ofObject(Property<?, V> property,
495            TypeConverter<PointF, V> converter, Path path) {
496        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
497        pvh.mKeyframes = KeyframeSet.ofPath(path);
498        pvh.mValueType = PointF.class;
499        pvh.setConverter(converter);
500        return pvh;
501    }
502
503    /**
504     * Constructs and returns a PropertyValuesHolder object with the specified property name and set
505     * of values. These values can be of any type, but the type should be consistent so that
506     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
507     * the common type.
508     * <p>If there is only one value, it is assumed to be the end value of an animation,
509     * and an initial value will be derived, if possible, by calling a getter function
510     * on the object. Also, if any value is null, the value will be filled in when the animation
511     * starts in the same way. This mechanism of automatically getting null values only works
512     * if the PropertyValuesHolder object is used in conjunction
513     * {@link ObjectAnimator}, and with a getter function
514     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
515     * no way of determining what the value should be.
516     * @param propertyName The name of the property associated with this set of values. This
517     * can be the actual property name to be used when using a ObjectAnimator object, or
518     * just a name used to get animated values, such as if this object is used with an
519     * ValueAnimator object.
520     * @param values The set of values to animate between.
521     */
522    public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
523        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
524        return ofKeyframes(propertyName, keyframeSet);
525    }
526
527    /**
528     * Constructs and returns a PropertyValuesHolder object with the specified property and set
529     * of values. These values can be of any type, but the type should be consistent so that
530     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
531     * the common type.
532     * <p>If there is only one value, it is assumed to be the end value of an animation,
533     * and an initial value will be derived, if possible, by calling the property's
534     * {@link android.util.Property#get(Object)} function.
535     * Also, if any value is null, the value will be filled in when the animation
536     * starts in the same way. This mechanism of automatically getting null values only works
537     * if the PropertyValuesHolder object is used in conjunction with
538     * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
539     * no way of determining what the value should be.
540     * @param property The property associated with this set of values. Should not be null.
541     * @param values The set of values to animate between.
542     */
543    public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
544        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
545        return ofKeyframes(property, keyframeSet);
546    }
547
548    static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) {
549        if (keyframes instanceof Keyframes.IntKeyframes) {
550            return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes);
551        } else if (keyframes instanceof Keyframes.FloatKeyframes) {
552            return new FloatPropertyValuesHolder(propertyName,
553                    (Keyframes.FloatKeyframes) keyframes);
554        } else {
555            PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
556            pvh.mKeyframes = keyframes;
557            pvh.mValueType = keyframes.getType();
558            return pvh;
559        }
560    }
561
562    static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) {
563        if (keyframes instanceof Keyframes.IntKeyframes) {
564            return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes);
565        } else if (keyframes instanceof Keyframes.FloatKeyframes) {
566            return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes);
567        } else {
568            PropertyValuesHolder pvh = new PropertyValuesHolder(property);
569            pvh.mKeyframes = keyframes;
570            pvh.mValueType = keyframes.getType();
571            return pvh;
572        }
573    }
574
575    /**
576     * Set the animated values for this object to this set of ints.
577     * If there is only one value, it is assumed to be the end value of an animation,
578     * and an initial value will be derived, if possible, by calling a getter function
579     * on the object. Also, if any value is null, the value will be filled in when the animation
580     * starts in the same way. This mechanism of automatically getting null values only works
581     * if the PropertyValuesHolder object is used in conjunction
582     * {@link ObjectAnimator}, and with a getter function
583     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
584     * no way of determining what the value should be.
585     *
586     * @param values One or more values that the animation will animate between.
587     */
588    public void setIntValues(int... values) {
589        mValueType = int.class;
590        mKeyframes = KeyframeSet.ofInt(values);
591    }
592
593    /**
594     * Set the animated values for this object to this set of floats.
595     * If there is only one value, it is assumed to be the end value of an animation,
596     * and an initial value will be derived, if possible, by calling a getter function
597     * on the object. Also, if any value is null, the value will be filled in when the animation
598     * starts in the same way. This mechanism of automatically getting null values only works
599     * if the PropertyValuesHolder object is used in conjunction
600     * {@link ObjectAnimator}, and with a getter function
601     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
602     * no way of determining what the value should be.
603     *
604     * @param values One or more values that the animation will animate between.
605     */
606    public void setFloatValues(float... values) {
607        mValueType = float.class;
608        mKeyframes = KeyframeSet.ofFloat(values);
609    }
610
611    /**
612     * Set the animated values for this object to this set of Keyframes.
613     *
614     * @param values One or more values that the animation will animate between.
615     */
616    public void setKeyframes(Keyframe... values) {
617        int numKeyframes = values.length;
618        Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
619        mValueType = ((Keyframe)values[0]).getType();
620        for (int i = 0; i < numKeyframes; ++i) {
621            keyframes[i] = (Keyframe)values[i];
622        }
623        mKeyframes = new KeyframeSet(keyframes);
624    }
625
626    /**
627     * Set the animated values for this object to this set of Objects.
628     * If there is only one value, it is assumed to be the end value of an animation,
629     * and an initial value will be derived, if possible, by calling a getter function
630     * on the object. Also, if any value is null, the value will be filled in when the animation
631     * starts in the same way. This mechanism of automatically getting null values only works
632     * if the PropertyValuesHolder object is used in conjunction
633     * {@link ObjectAnimator}, and with a getter function
634     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
635     * no way of determining what the value should be.
636     *
637     * @param values One or more values that the animation will animate between.
638     */
639    public void setObjectValues(Object... values) {
640        mValueType = values[0].getClass();
641        mKeyframes = KeyframeSet.ofObject(values);
642        if (mEvaluator != null) {
643            mKeyframes.setEvaluator(mEvaluator);
644        }
645    }
646
647    /**
648     * Sets the converter to convert from the values type to the setter's parameter type.
649     * If only one value is supplied, <var>converter</var> must be a
650     * {@link android.animation.BidirectionalTypeConverter}.
651     * @param converter The converter to use to convert values.
652     */
653    public void setConverter(TypeConverter converter) {
654        mConverter = converter;
655    }
656
657    /**
658     * Determine the setter or getter function using the JavaBeans convention of setFoo or
659     * getFoo for a property named 'foo'. This function figures out what the name of the
660     * function should be and uses reflection to find the Method with that name on the
661     * target object.
662     *
663     * @param targetClass The class to search for the method
664     * @param prefix "set" or "get", depending on whether we need a setter or getter.
665     * @param valueType The type of the parameter (in the case of a setter). This type
666     * is derived from the values set on this PropertyValuesHolder. This type is used as
667     * a first guess at the parameter type, but we check for methods with several different
668     * types to avoid problems with slight mis-matches between supplied values and actual
669     * value types used on the setter.
670     * @return Method the method associated with mPropertyName.
671     */
672    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
673        // TODO: faster implementation...
674        Method returnVal = null;
675        String methodName = getMethodName(prefix, mPropertyName);
676        Class args[] = null;
677        if (valueType == null) {
678            try {
679                returnVal = targetClass.getMethod(methodName, args);
680            } catch (NoSuchMethodException e) {
681                // Swallow the error, log it later
682            }
683        } else {
684            args = new Class[1];
685            Class typeVariants[];
686            if (valueType.equals(Float.class)) {
687                typeVariants = FLOAT_VARIANTS;
688            } else if (valueType.equals(Integer.class)) {
689                typeVariants = INTEGER_VARIANTS;
690            } else if (valueType.equals(Double.class)) {
691                typeVariants = DOUBLE_VARIANTS;
692            } else {
693                typeVariants = new Class[1];
694                typeVariants[0] = valueType;
695            }
696            for (Class typeVariant : typeVariants) {
697                args[0] = typeVariant;
698                try {
699                    returnVal = targetClass.getMethod(methodName, args);
700                    if (mConverter == null) {
701                        // change the value type to suit
702                        mValueType = typeVariant;
703                    }
704                    return returnVal;
705                } catch (NoSuchMethodException e) {
706                    // Swallow the error and keep trying other variants
707                }
708            }
709            // If we got here, then no appropriate function was found
710        }
711
712        if (returnVal == null) {
713            Log.w("PropertyValuesHolder", "Method " +
714                    getMethodName(prefix, mPropertyName) + "() with type " + valueType +
715                    " not found on target class " + targetClass);
716        }
717
718        return returnVal;
719    }
720
721
722    /**
723     * Returns the setter or getter requested. This utility function checks whether the
724     * requested method exists in the propertyMapMap cache. If not, it calls another
725     * utility function to request the Method from the targetClass directly.
726     * @param targetClass The Class on which the requested method should exist.
727     * @param propertyMapMap The cache of setters/getters derived so far.
728     * @param prefix "set" or "get", for the setter or getter.
729     * @param valueType The type of parameter passed into the method (null for getter).
730     * @return Method the method associated with mPropertyName.
731     */
732    private Method setupSetterOrGetter(Class targetClass,
733            HashMap<Class, HashMap<String, Method>> propertyMapMap,
734            String prefix, Class valueType) {
735        Method setterOrGetter = null;
736        synchronized(propertyMapMap) {
737            // Have to lock property map prior to reading it, to guard against
738            // another thread putting something in there after we've checked it
739            // but before we've added an entry to it
740            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
741            boolean wasInMap = false;
742            if (propertyMap != null) {
743                wasInMap = propertyMap.containsKey(mPropertyName);
744                if (wasInMap) {
745                    setterOrGetter = propertyMap.get(mPropertyName);
746                }
747            }
748            if (!wasInMap) {
749                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
750                if (propertyMap == null) {
751                    propertyMap = new HashMap<String, Method>();
752                    propertyMapMap.put(targetClass, propertyMap);
753                }
754                propertyMap.put(mPropertyName, setterOrGetter);
755            }
756        }
757        return setterOrGetter;
758    }
759
760    /**
761     * Utility function to get the setter from targetClass
762     * @param targetClass The Class on which the requested method should exist.
763     */
764    void setupSetter(Class targetClass) {
765        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
766        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
767    }
768
769    /**
770     * Utility function to get the getter from targetClass
771     */
772    private void setupGetter(Class targetClass) {
773        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
774    }
775
776    /**
777     * Internal function (called from ObjectAnimator) to set up the setter and getter
778     * prior to running the animation. If the setter has not been manually set for this
779     * object, it will be derived automatically given the property name, target object, and
780     * types of values supplied. If no getter has been set, it will be supplied iff any of the
781     * supplied values was null. If there is a null value, then the getter (supplied or derived)
782     * will be called to set those null values to the current value of the property
783     * on the target object.
784     * @param target The object on which the setter (and possibly getter) exist.
785     */
786    void setupSetterAndGetter(Object target) {
787        mKeyframes.invalidateCache();
788        if (mProperty != null) {
789            // check to make sure that mProperty is on the class of target
790            try {
791                Object testValue = null;
792                List<Keyframe> keyframes = mKeyframes.getKeyframes();
793                int keyframeCount = keyframes == null ? 0 : keyframes.size();
794                for (int i = 0; i < keyframeCount; i++) {
795                    Keyframe kf = keyframes.get(i);
796                    if (!kf.hasValue() || kf.valueWasSetOnStart()) {
797                        if (testValue == null) {
798                            testValue = convertBack(mProperty.get(target));
799                        }
800                        kf.setValue(testValue);
801                        kf.setValueWasSetOnStart(true);
802                    }
803                }
804                return;
805            } catch (ClassCastException e) {
806                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
807                        ") on target object " + target + ". Trying reflection instead");
808                mProperty = null;
809            }
810        }
811        // We can't just say 'else' here because the catch statement sets mProperty to null.
812        if (mProperty == null) {
813            Class targetClass = target.getClass();
814            if (mSetter == null) {
815                setupSetter(targetClass);
816            }
817            List<Keyframe> keyframes = mKeyframes.getKeyframes();
818            int keyframeCount = keyframes == null ? 0 : keyframes.size();
819            for (int i = 0; i < keyframeCount; i++) {
820                Keyframe kf = keyframes.get(i);
821                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
822                    if (mGetter == null) {
823                        setupGetter(targetClass);
824                        if (mGetter == null) {
825                            // Already logged the error - just return to avoid NPE
826                            return;
827                        }
828                    }
829                    try {
830                        Object value = convertBack(mGetter.invoke(target));
831                        kf.setValue(value);
832                        kf.setValueWasSetOnStart(true);
833                    } catch (InvocationTargetException e) {
834                        Log.e("PropertyValuesHolder", e.toString());
835                    } catch (IllegalAccessException e) {
836                        Log.e("PropertyValuesHolder", e.toString());
837                    }
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            synchronized(sJNISetterPropertyMap) {
1182                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1183                boolean wasInMap = false;
1184                if (propertyMap != null) {
1185                    wasInMap = propertyMap.containsKey(mPropertyName);
1186                    if (wasInMap) {
1187                        Long jniSetter = propertyMap.get(mPropertyName);
1188                        if (jniSetter != null) {
1189                            mJniSetter = jniSetter;
1190                        }
1191                    }
1192                }
1193                if (!wasInMap) {
1194                    String methodName = getMethodName("set", mPropertyName);
1195                    try {
1196                        mJniSetter = nGetIntMethod(targetClass, methodName);
1197                    } catch (NoSuchMethodError e) {
1198                        // Couldn't find it via JNI - try reflection next. Probably means the method
1199                        // doesn't exist, or the type is wrong. An error will be logged later if
1200                        // reflection fails as well.
1201                    }
1202                    if (propertyMap == null) {
1203                        propertyMap = new HashMap<String, Long>();
1204                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1205                    }
1206                    propertyMap.put(mPropertyName, mJniSetter);
1207                }
1208            }
1209            if (mJniSetter == 0) {
1210                // Couldn't find method through fast JNI approach - just use reflection
1211                super.setupSetter(targetClass);
1212            }
1213        }
1214    }
1215
1216    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1217
1218        // Cache JNI functions to avoid looking them up twice
1219        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1220                new HashMap<Class, HashMap<String, Long>>();
1221        long mJniSetter;
1222        private FloatProperty mFloatProperty;
1223
1224        Keyframes.FloatKeyframes mFloatKeyframes;
1225        float mFloatAnimatedValue;
1226
1227        public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
1228            super(propertyName);
1229            mValueType = float.class;
1230            mKeyframes = keyframes;
1231            mFloatKeyframes = keyframes;
1232        }
1233
1234        public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
1235            super(property);
1236            mValueType = float.class;
1237            mKeyframes = keyframes;
1238            mFloatKeyframes = keyframes;
1239            if (property instanceof FloatProperty) {
1240                mFloatProperty = (FloatProperty) mProperty;
1241            }
1242        }
1243
1244        public FloatPropertyValuesHolder(String propertyName, float... values) {
1245            super(propertyName);
1246            setFloatValues(values);
1247        }
1248
1249        public FloatPropertyValuesHolder(Property property, float... values) {
1250            super(property);
1251            setFloatValues(values);
1252            if (property instanceof  FloatProperty) {
1253                mFloatProperty = (FloatProperty) mProperty;
1254            }
1255        }
1256
1257        @Override
1258        public void setFloatValues(float... values) {
1259            super.setFloatValues(values);
1260            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
1261        }
1262
1263        @Override
1264        void calculateValue(float fraction) {
1265            mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
1266        }
1267
1268        @Override
1269        Object getAnimatedValue() {
1270            return mFloatAnimatedValue;
1271        }
1272
1273        @Override
1274        public FloatPropertyValuesHolder clone() {
1275            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1276            newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
1277            return newPVH;
1278        }
1279
1280        /**
1281         * Internal function to set the value on the target object, using the setter set up
1282         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1283         * to handle turning the value calculated by ValueAnimator into a value set on the object
1284         * according to the name of the property.
1285         * @param target The target object on which the value is set
1286         */
1287        @Override
1288        void setAnimatedValue(Object target) {
1289            if (mFloatProperty != null) {
1290                mFloatProperty.setValue(target, mFloatAnimatedValue);
1291                return;
1292            }
1293            if (mProperty != null) {
1294                mProperty.set(target, mFloatAnimatedValue);
1295                return;
1296            }
1297            if (mJniSetter != 0) {
1298                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1299                return;
1300            }
1301            if (mSetter != null) {
1302                try {
1303                    mTmpValueArray[0] = mFloatAnimatedValue;
1304                    mSetter.invoke(target, mTmpValueArray);
1305                } catch (InvocationTargetException e) {
1306                    Log.e("PropertyValuesHolder", e.toString());
1307                } catch (IllegalAccessException e) {
1308                    Log.e("PropertyValuesHolder", e.toString());
1309                }
1310            }
1311        }
1312
1313        @Override
1314        void setupSetter(Class targetClass) {
1315            if (mProperty != null) {
1316                return;
1317            }
1318            // Check new static hashmap<propName, int> for setter method
1319            synchronized (sJNISetterPropertyMap) {
1320                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1321                boolean wasInMap = false;
1322                if (propertyMap != null) {
1323                    wasInMap = propertyMap.containsKey(mPropertyName);
1324                    if (wasInMap) {
1325                        Long jniSetter = propertyMap.get(mPropertyName);
1326                        if (jniSetter != null) {
1327                            mJniSetter = jniSetter;
1328                        }
1329                    }
1330                }
1331                if (!wasInMap) {
1332                    String methodName = getMethodName("set", mPropertyName);
1333                    try {
1334                        mJniSetter = nGetFloatMethod(targetClass, methodName);
1335                    } catch (NoSuchMethodError e) {
1336                        // Couldn't find it via JNI - try reflection next. Probably means the method
1337                        // doesn't exist, or the type is wrong. An error will be logged later if
1338                        // reflection fails as well.
1339                    }
1340                    if (propertyMap == null) {
1341                        propertyMap = new HashMap<String, Long>();
1342                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1343                    }
1344                    propertyMap.put(mPropertyName, mJniSetter);
1345                }
1346            }
1347            if (mJniSetter == 0) {
1348                // Couldn't find method through fast JNI approach - just use reflection
1349                super.setupSetter(targetClass);
1350            }
1351        }
1352
1353    }
1354
1355    static class MultiFloatValuesHolder extends PropertyValuesHolder {
1356        private long mJniSetter;
1357        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1358                new HashMap<Class, HashMap<String, Long>>();
1359
1360        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1361                TypeEvaluator evaluator, Object... values) {
1362            super(propertyName);
1363            setConverter(converter);
1364            setObjectValues(values);
1365            setEvaluator(evaluator);
1366        }
1367
1368        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1369                TypeEvaluator evaluator, Keyframes keyframes) {
1370            super(propertyName);
1371            setConverter(converter);
1372            mKeyframes = keyframes;
1373            setEvaluator(evaluator);
1374        }
1375
1376        /**
1377         * Internal function to set the value on the target object, using the setter set up
1378         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1379         * to handle turning the value calculated by ValueAnimator into a value set on the object
1380         * according to the name of the property.
1381         *
1382         * @param target The target object on which the value is set
1383         */
1384        @Override
1385        void setAnimatedValue(Object target) {
1386            float[] values = (float[]) getAnimatedValue();
1387            int numParameters = values.length;
1388            if (mJniSetter != 0) {
1389                switch (numParameters) {
1390                    case 1:
1391                        nCallFloatMethod(target, mJniSetter, values[0]);
1392                        break;
1393                    case 2:
1394                        nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1395                        break;
1396                    case 4:
1397                        nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1398                                values[2], values[3]);
1399                        break;
1400                    default: {
1401                        nCallMultipleFloatMethod(target, mJniSetter, values);
1402                        break;
1403                    }
1404                }
1405            }
1406        }
1407
1408        /**
1409         * Internal function (called from ObjectAnimator) to set up the setter and getter
1410         * prior to running the animation. No getter can be used for multiple parameters.
1411         *
1412         * @param target The object on which the setter exists.
1413         */
1414        @Override
1415        void setupSetterAndGetter(Object target) {
1416            setupSetter(target.getClass());
1417        }
1418
1419        @Override
1420        void setupSetter(Class targetClass) {
1421            if (mJniSetter != 0) {
1422                return;
1423            }
1424            synchronized(sJNISetterPropertyMap) {
1425                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1426                boolean wasInMap = false;
1427                if (propertyMap != null) {
1428                    wasInMap = propertyMap.containsKey(mPropertyName);
1429                    if (wasInMap) {
1430                        Long jniSetter = propertyMap.get(mPropertyName);
1431                        if (jniSetter != null) {
1432                            mJniSetter = jniSetter;
1433                        }
1434                    }
1435                }
1436                if (!wasInMap) {
1437                    String methodName = getMethodName("set", mPropertyName);
1438                    calculateValue(0f);
1439                    float[] values = (float[]) getAnimatedValue();
1440                    int numParams = values.length;
1441                    try {
1442                        mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1443                    } catch (NoSuchMethodError e) {
1444                        // try without the 'set' prefix
1445                        try {
1446                            mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
1447                                    numParams);
1448                        } catch (NoSuchMethodError e2) {
1449                            // just try reflection next
1450                        }
1451                    }
1452                    if (propertyMap == null) {
1453                        propertyMap = new HashMap<String, Long>();
1454                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1455                    }
1456                    propertyMap.put(mPropertyName, mJniSetter);
1457                }
1458           }
1459        }
1460    }
1461
1462    static class MultiIntValuesHolder extends PropertyValuesHolder {
1463        private long mJniSetter;
1464        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1465                new HashMap<Class, HashMap<String, Long>>();
1466
1467        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1468                TypeEvaluator evaluator, Object... values) {
1469            super(propertyName);
1470            setConverter(converter);
1471            setObjectValues(values);
1472            setEvaluator(evaluator);
1473        }
1474
1475        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1476                TypeEvaluator evaluator, Keyframes keyframes) {
1477            super(propertyName);
1478            setConverter(converter);
1479            mKeyframes = keyframes;
1480            setEvaluator(evaluator);
1481        }
1482
1483        /**
1484         * Internal function to set the value on the target object, using the setter set up
1485         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1486         * to handle turning the value calculated by ValueAnimator into a value set on the object
1487         * according to the name of the property.
1488         *
1489         * @param target The target object on which the value is set
1490         */
1491        @Override
1492        void setAnimatedValue(Object target) {
1493            int[] values = (int[]) getAnimatedValue();
1494            int numParameters = values.length;
1495            if (mJniSetter != 0) {
1496                switch (numParameters) {
1497                    case 1:
1498                        nCallIntMethod(target, mJniSetter, values[0]);
1499                        break;
1500                    case 2:
1501                        nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1502                        break;
1503                    case 4:
1504                        nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1505                                values[2], values[3]);
1506                        break;
1507                    default: {
1508                        nCallMultipleIntMethod(target, mJniSetter, values);
1509                        break;
1510                    }
1511                }
1512            }
1513        }
1514
1515        /**
1516         * Internal function (called from ObjectAnimator) to set up the setter and getter
1517         * prior to running the animation. No getter can be used for multiple parameters.
1518         *
1519         * @param target The object on which the setter exists.
1520         */
1521        @Override
1522        void setupSetterAndGetter(Object target) {
1523            setupSetter(target.getClass());
1524        }
1525
1526        @Override
1527        void setupSetter(Class targetClass) {
1528            if (mJniSetter != 0) {
1529                return;
1530            }
1531            synchronized(sJNISetterPropertyMap) {
1532                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1533                boolean wasInMap = false;
1534                if (propertyMap != null) {
1535                    wasInMap = propertyMap.containsKey(mPropertyName);
1536                    if (wasInMap) {
1537                        Long jniSetter = propertyMap.get(mPropertyName);
1538                        if (jniSetter != null) {
1539                            mJniSetter = jniSetter;
1540                        }
1541                    }
1542                }
1543                if (!wasInMap) {
1544                    String methodName = getMethodName("set", mPropertyName);
1545                    calculateValue(0f);
1546                    int[] values = (int[]) getAnimatedValue();
1547                    int numParams = values.length;
1548                    try {
1549                        mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1550                    } catch (NoSuchMethodError e) {
1551                        // try without the 'set' prefix
1552                        try {
1553                            mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
1554                                    numParams);
1555                        } catch (NoSuchMethodError e2) {
1556                            // couldn't find it.
1557                        }
1558                    }
1559                    if (propertyMap == null) {
1560                        propertyMap = new HashMap<String, Long>();
1561                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1562                    }
1563                    propertyMap.put(mPropertyName, mJniSetter);
1564                }
1565            }
1566        }
1567    }
1568
1569    /**
1570     * Convert from PointF to float[] for multi-float setters along a Path.
1571     */
1572    private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1573        private float[] mCoordinates = new float[2];
1574
1575        public PointFToFloatArray() {
1576            super(PointF.class, float[].class);
1577        }
1578
1579        @Override
1580        public float[] convert(PointF value) {
1581            mCoordinates[0] = value.x;
1582            mCoordinates[1] = value.y;
1583            return mCoordinates;
1584        }
1585    };
1586
1587    /**
1588     * Convert from PointF to int[] for multi-int setters along a Path.
1589     */
1590    private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1591        private int[] mCoordinates = new int[2];
1592
1593        public PointFToIntArray() {
1594            super(PointF.class, int[].class);
1595        }
1596
1597        @Override
1598        public int[] convert(PointF value) {
1599            mCoordinates[0] = Math.round(value.x);
1600            mCoordinates[1] = Math.round(value.y);
1601            return mCoordinates;
1602        }
1603    };
1604
1605    native static private long nGetIntMethod(Class targetClass, String methodName);
1606    native static private long nGetFloatMethod(Class targetClass, String methodName);
1607    native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
1608            int numParams);
1609    native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
1610            int numParams);
1611    native static private void nCallIntMethod(Object target, long methodID, int arg);
1612    native static private void nCallFloatMethod(Object target, long methodID, float arg);
1613    native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
1614    native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
1615            int arg3, int arg4);
1616    native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
1617    native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
1618            float arg2);
1619    native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
1620            float arg2, float arg3, float arg4);
1621    native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
1622}
1623