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