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