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