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        mKeyframes.invalidateCache();
811        if (mProperty != null) {
812            // check to make sure that mProperty is on the class of target
813            try {
814                Object testValue = null;
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 (testValue == null) {
821                            testValue = convertBack(mProperty.get(target));
822                        }
823                        kf.setValue(testValue);
824                        kf.setValueWasSetOnStart(true);
825                    }
826                }
827                return;
828            } catch (ClassCastException e) {
829                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
830                        ") on target object " + target + ". Trying reflection instead");
831                mProperty = null;
832            }
833        }
834        // We can't just say 'else' here because the catch statement sets mProperty to null.
835        if (mProperty == null) {
836            Class targetClass = target.getClass();
837            if (mSetter == null) {
838                setupSetter(targetClass);
839            }
840            List<Keyframe> keyframes = mKeyframes.getKeyframes();
841            int keyframeCount = keyframes == null ? 0 : keyframes.size();
842            for (int i = 0; i < keyframeCount; i++) {
843                Keyframe kf = keyframes.get(i);
844                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
845                    if (mGetter == null) {
846                        setupGetter(targetClass);
847                        if (mGetter == null) {
848                            // Already logged the error - just return to avoid NPE
849                            return;
850                        }
851                    }
852                    try {
853                        Object value = convertBack(mGetter.invoke(target));
854                        kf.setValue(value);
855                        kf.setValueWasSetOnStart(true);
856                    } catch (InvocationTargetException e) {
857                        Log.e("PropertyValuesHolder", e.toString());
858                    } catch (IllegalAccessException e) {
859                        Log.e("PropertyValuesHolder", e.toString());
860                    }
861                }
862            }
863        }
864    }
865
866    private Object convertBack(Object value) {
867        if (mConverter != null) {
868            if (!(mConverter instanceof BidirectionalTypeConverter)) {
869                throw new IllegalArgumentException("Converter "
870                        + mConverter.getClass().getName()
871                        + " must be a BidirectionalTypeConverter");
872            }
873            value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
874        }
875        return value;
876    }
877
878    /**
879     * Utility function to set the value stored in a particular Keyframe. The value used is
880     * whatever the value is for the property name specified in the keyframe on the target object.
881     *
882     * @param target The target object from which the current value should be extracted.
883     * @param kf The keyframe which holds the property name and value.
884     */
885    private void setupValue(Object target, Keyframe kf) {
886        if (mProperty != null) {
887            Object value = convertBack(mProperty.get(target));
888            kf.setValue(value);
889        } else {
890            try {
891                if (mGetter == null) {
892                    Class targetClass = target.getClass();
893                    setupGetter(targetClass);
894                    if (mGetter == null) {
895                        // Already logged the error - just return to avoid NPE
896                        return;
897                    }
898                }
899                Object value = convertBack(mGetter.invoke(target));
900                kf.setValue(value);
901            } catch (InvocationTargetException e) {
902                Log.e("PropertyValuesHolder", e.toString());
903            } catch (IllegalAccessException e) {
904                Log.e("PropertyValuesHolder", e.toString());
905            }
906        }
907    }
908
909    /**
910     * This function is called by ObjectAnimator when setting the start values for an animation.
911     * The start values are set according to the current values in the target object. The
912     * property whose value is extracted is whatever is specified by the propertyName of this
913     * PropertyValuesHolder object.
914     *
915     * @param target The object which holds the start values that should be set.
916     */
917    void setupStartValue(Object target) {
918        List<Keyframe> keyframes = mKeyframes.getKeyframes();
919        if (!keyframes.isEmpty()) {
920            setupValue(target, keyframes.get(0));
921        }
922    }
923
924    /**
925     * This function is called by ObjectAnimator when setting the end values for an animation.
926     * The end values are set according to the current values in the target object. The
927     * property whose value is extracted is whatever is specified by the propertyName of this
928     * PropertyValuesHolder object.
929     *
930     * @param target The object which holds the start values that should be set.
931     */
932    void setupEndValue(Object target) {
933        List<Keyframe> keyframes = mKeyframes.getKeyframes();
934        if (!keyframes.isEmpty()) {
935            setupValue(target, keyframes.get(keyframes.size() - 1));
936        }
937    }
938
939    @Override
940    public PropertyValuesHolder clone() {
941        try {
942            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
943            newPVH.mPropertyName = mPropertyName;
944            newPVH.mProperty = mProperty;
945            newPVH.mKeyframes = mKeyframes.clone();
946            newPVH.mEvaluator = mEvaluator;
947            return newPVH;
948        } catch (CloneNotSupportedException e) {
949            // won't reach here
950            return null;
951        }
952    }
953
954    /**
955     * Internal function to set the value on the target object, using the setter set up
956     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
957     * to handle turning the value calculated by ValueAnimator into a value set on the object
958     * according to the name of the property.
959     * @param target The target object on which the value is set
960     */
961    void setAnimatedValue(Object target) {
962        if (mProperty != null) {
963            mProperty.set(target, getAnimatedValue());
964        }
965        if (mSetter != null) {
966            try {
967                mTmpValueArray[0] = getAnimatedValue();
968                mSetter.invoke(target, mTmpValueArray);
969            } catch (InvocationTargetException e) {
970                Log.e("PropertyValuesHolder", e.toString());
971            } catch (IllegalAccessException e) {
972                Log.e("PropertyValuesHolder", e.toString());
973            }
974        }
975    }
976
977    /**
978     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
979     * to calculate animated values.
980     */
981    void init() {
982        if (mEvaluator == null) {
983            // We already handle int and float automatically, but not their Object
984            // equivalents
985            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
986                    (mValueType == Float.class) ? sFloatEvaluator :
987                    null;
988        }
989        if (mEvaluator != null) {
990            // KeyframeSet knows how to evaluate the common types - only give it a custom
991            // evaluator if one has been set on this class
992            mKeyframes.setEvaluator(mEvaluator);
993        }
994    }
995
996    /**
997     * The TypeEvaluator will be automatically determined based on the type of values
998     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
999     * desired. This may be important in cases where either the type of the values supplied
1000     * do not match the way that they should be interpolated between, or if the values
1001     * are of a custom type or one not currently understood by the animation system. Currently,
1002     * only values of type float and int (and their Object equivalents: Float
1003     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
1004     * @param evaluator
1005     */
1006    public void setEvaluator(TypeEvaluator evaluator) {
1007        mEvaluator = evaluator;
1008        mKeyframes.setEvaluator(evaluator);
1009    }
1010
1011    /**
1012     * Function used to calculate the value according to the evaluator set up for
1013     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
1014     *
1015     * @param fraction The elapsed, interpolated fraction of the animation.
1016     */
1017    void calculateValue(float fraction) {
1018        Object value = mKeyframes.getValue(fraction);
1019        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
1020    }
1021
1022    /**
1023     * Sets the name of the property that will be animated. This name is used to derive
1024     * a setter function that will be called to set animated values.
1025     * For example, a property name of <code>foo</code> will result
1026     * in a call to the function <code>setFoo()</code> on the target object. If either
1027     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1028     * also be derived and called.
1029     *
1030     * <p>Note that the setter function derived from this property name
1031     * must take the same parameter type as the
1032     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
1033     * the setter function will fail.</p>
1034     *
1035     * @param propertyName The name of the property being animated.
1036     */
1037    public void setPropertyName(String propertyName) {
1038        mPropertyName = propertyName;
1039    }
1040
1041    /**
1042     * Sets the property that will be animated.
1043     *
1044     * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
1045     * must exist on the target object specified in that ObjectAnimator.</p>
1046     *
1047     * @param property The property being animated.
1048     */
1049    public void setProperty(Property property) {
1050        mProperty = property;
1051    }
1052
1053    /**
1054     * Gets the name of the property that will be animated. This name will be used to derive
1055     * a setter function that will be called to set animated values.
1056     * For example, a property name of <code>foo</code> will result
1057     * in a call to the function <code>setFoo()</code> on the target object. If either
1058     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1059     * also be derived and called.
1060     */
1061    public String getPropertyName() {
1062        return mPropertyName;
1063    }
1064
1065    /**
1066     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
1067     * most recently calculated in calculateValue().
1068     * @return
1069     */
1070    Object getAnimatedValue() {
1071        return mAnimatedValue;
1072    }
1073
1074    /**
1075     * PropertyValuesHolder is Animators use to hold internal animation related data.
1076     * Therefore, in order to replicate the animation behavior, we need to get data out of
1077     * PropertyValuesHolder.
1078     * @hide
1079     */
1080    public void getPropertyValues(PropertyValues values) {
1081        init();
1082        values.propertyName = mPropertyName;
1083        values.type = mValueType;
1084        values.startValue = mKeyframes.getValue(0);
1085        if (values.startValue instanceof PathParser.PathData) {
1086            // PathData evaluator returns the same mutable PathData object when query fraction,
1087            // so we have to make a copy here.
1088            values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
1089        }
1090        values.endValue = mKeyframes.getValue(1);
1091        if (values.endValue instanceof PathParser.PathData) {
1092            // PathData evaluator returns the same mutable PathData object when query fraction,
1093            // so we have to make a copy here.
1094            values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
1095        }
1096        // TODO: We need a better way to get data out of keyframes.
1097        if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
1098                || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
1099            // property values will animate based on external data source (e.g. Path)
1100            values.dataSource = new PropertyValues.DataSource() {
1101                @Override
1102                public Object getValueAtFraction(float fraction) {
1103                    return mKeyframes.getValue(fraction);
1104                }
1105            };
1106        } else {
1107            values.dataSource = null;
1108        }
1109    }
1110
1111    @Override
1112    public String toString() {
1113        return mPropertyName + ": " + mKeyframes.toString();
1114    }
1115
1116    /**
1117     * Utility method to derive a setter/getter method name from a property name, where the
1118     * prefix is typically "set" or "get" and the first letter of the property name is
1119     * capitalized.
1120     *
1121     * @param prefix The precursor to the method name, before the property name begins, typically
1122     * "set" or "get".
1123     * @param propertyName The name of the property that represents the bulk of the method name
1124     * after the prefix. The first letter of this word will be capitalized in the resulting
1125     * method name.
1126     * @return String the property name converted to a method name according to the conventions
1127     * specified above.
1128     */
1129    static String getMethodName(String prefix, String propertyName) {
1130        if (propertyName == null || propertyName.length() == 0) {
1131            // shouldn't get here
1132            return prefix;
1133        }
1134        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
1135        String theRest = propertyName.substring(1);
1136        return prefix + firstLetter + theRest;
1137    }
1138
1139    static class IntPropertyValuesHolder extends PropertyValuesHolder {
1140
1141        // Cache JNI functions to avoid looking them up twice
1142        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1143                new HashMap<Class, HashMap<String, Long>>();
1144        long mJniSetter;
1145        private IntProperty mIntProperty;
1146
1147        Keyframes.IntKeyframes mIntKeyframes;
1148        int mIntAnimatedValue;
1149
1150        public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
1151            super(propertyName);
1152            mValueType = int.class;
1153            mKeyframes = keyframes;
1154            mIntKeyframes = keyframes;
1155        }
1156
1157        public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
1158            super(property);
1159            mValueType = int.class;
1160            mKeyframes = keyframes;
1161            mIntKeyframes = keyframes;
1162            if (property instanceof  IntProperty) {
1163                mIntProperty = (IntProperty) mProperty;
1164            }
1165        }
1166
1167        public IntPropertyValuesHolder(String propertyName, int... values) {
1168            super(propertyName);
1169            setIntValues(values);
1170        }
1171
1172        public IntPropertyValuesHolder(Property property, int... values) {
1173            super(property);
1174            setIntValues(values);
1175            if (property instanceof  IntProperty) {
1176                mIntProperty = (IntProperty) mProperty;
1177            }
1178        }
1179
1180        @Override
1181        public void setIntValues(int... values) {
1182            super.setIntValues(values);
1183            mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
1184        }
1185
1186        @Override
1187        void calculateValue(float fraction) {
1188            mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
1189        }
1190
1191        @Override
1192        Object getAnimatedValue() {
1193            return mIntAnimatedValue;
1194        }
1195
1196        @Override
1197        public IntPropertyValuesHolder clone() {
1198            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
1199            newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
1200            return newPVH;
1201        }
1202
1203        /**
1204         * Internal function to set the value on the target object, using the setter set up
1205         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1206         * to handle turning the value calculated by ValueAnimator into a value set on the object
1207         * according to the name of the property.
1208         * @param target The target object on which the value is set
1209         */
1210        @Override
1211        void setAnimatedValue(Object target) {
1212            if (mIntProperty != null) {
1213                mIntProperty.setValue(target, mIntAnimatedValue);
1214                return;
1215            }
1216            if (mProperty != null) {
1217                mProperty.set(target, mIntAnimatedValue);
1218                return;
1219            }
1220            if (mJniSetter != 0) {
1221                nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1222                return;
1223            }
1224            if (mSetter != null) {
1225                try {
1226                    mTmpValueArray[0] = mIntAnimatedValue;
1227                    mSetter.invoke(target, mTmpValueArray);
1228                } catch (InvocationTargetException e) {
1229                    Log.e("PropertyValuesHolder", e.toString());
1230                } catch (IllegalAccessException e) {
1231                    Log.e("PropertyValuesHolder", e.toString());
1232                }
1233            }
1234        }
1235
1236        @Override
1237        void setupSetter(Class targetClass) {
1238            if (mProperty != null) {
1239                return;
1240            }
1241            // Check new static hashmap<propName, int> for setter method
1242            synchronized(sJNISetterPropertyMap) {
1243                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1244                boolean wasInMap = false;
1245                if (propertyMap != null) {
1246                    wasInMap = propertyMap.containsKey(mPropertyName);
1247                    if (wasInMap) {
1248                        Long jniSetter = propertyMap.get(mPropertyName);
1249                        if (jniSetter != null) {
1250                            mJniSetter = jniSetter;
1251                        }
1252                    }
1253                }
1254                if (!wasInMap) {
1255                    String methodName = getMethodName("set", mPropertyName);
1256                    try {
1257                        mJniSetter = nGetIntMethod(targetClass, methodName);
1258                    } catch (NoSuchMethodError e) {
1259                        // Couldn't find it via JNI - try reflection next. Probably means the method
1260                        // doesn't exist, or the type is wrong. An error will be logged later if
1261                        // reflection fails as well.
1262                    }
1263                    if (propertyMap == null) {
1264                        propertyMap = new HashMap<String, Long>();
1265                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1266                    }
1267                    propertyMap.put(mPropertyName, mJniSetter);
1268                }
1269            }
1270            if (mJniSetter == 0) {
1271                // Couldn't find method through fast JNI approach - just use reflection
1272                super.setupSetter(targetClass);
1273            }
1274        }
1275    }
1276
1277    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1278
1279        // Cache JNI functions to avoid looking them up twice
1280        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1281                new HashMap<Class, HashMap<String, Long>>();
1282        long mJniSetter;
1283        private FloatProperty mFloatProperty;
1284
1285        Keyframes.FloatKeyframes mFloatKeyframes;
1286        float mFloatAnimatedValue;
1287
1288        public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
1289            super(propertyName);
1290            mValueType = float.class;
1291            mKeyframes = keyframes;
1292            mFloatKeyframes = keyframes;
1293        }
1294
1295        public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
1296            super(property);
1297            mValueType = float.class;
1298            mKeyframes = keyframes;
1299            mFloatKeyframes = keyframes;
1300            if (property instanceof FloatProperty) {
1301                mFloatProperty = (FloatProperty) mProperty;
1302            }
1303        }
1304
1305        public FloatPropertyValuesHolder(String propertyName, float... values) {
1306            super(propertyName);
1307            setFloatValues(values);
1308        }
1309
1310        public FloatPropertyValuesHolder(Property property, float... values) {
1311            super(property);
1312            setFloatValues(values);
1313            if (property instanceof  FloatProperty) {
1314                mFloatProperty = (FloatProperty) mProperty;
1315            }
1316        }
1317
1318        @Override
1319        public void setFloatValues(float... values) {
1320            super.setFloatValues(values);
1321            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
1322        }
1323
1324        @Override
1325        void calculateValue(float fraction) {
1326            mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
1327        }
1328
1329        @Override
1330        Object getAnimatedValue() {
1331            return mFloatAnimatedValue;
1332        }
1333
1334        @Override
1335        public FloatPropertyValuesHolder clone() {
1336            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1337            newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
1338            return newPVH;
1339        }
1340
1341        /**
1342         * Internal function to set the value on the target object, using the setter set up
1343         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1344         * to handle turning the value calculated by ValueAnimator into a value set on the object
1345         * according to the name of the property.
1346         * @param target The target object on which the value is set
1347         */
1348        @Override
1349        void setAnimatedValue(Object target) {
1350            if (mFloatProperty != null) {
1351                mFloatProperty.setValue(target, mFloatAnimatedValue);
1352                return;
1353            }
1354            if (mProperty != null) {
1355                mProperty.set(target, mFloatAnimatedValue);
1356                return;
1357            }
1358            if (mJniSetter != 0) {
1359                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1360                return;
1361            }
1362            if (mSetter != null) {
1363                try {
1364                    mTmpValueArray[0] = mFloatAnimatedValue;
1365                    mSetter.invoke(target, mTmpValueArray);
1366                } catch (InvocationTargetException e) {
1367                    Log.e("PropertyValuesHolder", e.toString());
1368                } catch (IllegalAccessException e) {
1369                    Log.e("PropertyValuesHolder", e.toString());
1370                }
1371            }
1372        }
1373
1374        @Override
1375        void setupSetter(Class targetClass) {
1376            if (mProperty != null) {
1377                return;
1378            }
1379            // Check new static hashmap<propName, int> for setter method
1380            synchronized (sJNISetterPropertyMap) {
1381                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1382                boolean wasInMap = false;
1383                if (propertyMap != null) {
1384                    wasInMap = propertyMap.containsKey(mPropertyName);
1385                    if (wasInMap) {
1386                        Long jniSetter = propertyMap.get(mPropertyName);
1387                        if (jniSetter != null) {
1388                            mJniSetter = jniSetter;
1389                        }
1390                    }
1391                }
1392                if (!wasInMap) {
1393                    String methodName = getMethodName("set", mPropertyName);
1394                    try {
1395                        mJniSetter = nGetFloatMethod(targetClass, methodName);
1396                    } catch (NoSuchMethodError e) {
1397                        // Couldn't find it via JNI - try reflection next. Probably means the method
1398                        // doesn't exist, or the type is wrong. An error will be logged later if
1399                        // reflection fails as well.
1400                    }
1401                    if (propertyMap == null) {
1402                        propertyMap = new HashMap<String, Long>();
1403                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1404                    }
1405                    propertyMap.put(mPropertyName, mJniSetter);
1406                }
1407            }
1408            if (mJniSetter == 0) {
1409                // Couldn't find method through fast JNI approach - just use reflection
1410                super.setupSetter(targetClass);
1411            }
1412        }
1413
1414    }
1415
1416    static class MultiFloatValuesHolder extends PropertyValuesHolder {
1417        private long mJniSetter;
1418        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1419                new HashMap<Class, HashMap<String, Long>>();
1420
1421        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1422                TypeEvaluator evaluator, Object... values) {
1423            super(propertyName);
1424            setConverter(converter);
1425            setObjectValues(values);
1426            setEvaluator(evaluator);
1427        }
1428
1429        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1430                TypeEvaluator evaluator, Keyframes keyframes) {
1431            super(propertyName);
1432            setConverter(converter);
1433            mKeyframes = keyframes;
1434            setEvaluator(evaluator);
1435        }
1436
1437        /**
1438         * Internal function to set the value on the target object, using the setter set up
1439         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1440         * to handle turning the value calculated by ValueAnimator into a value set on the object
1441         * according to the name of the property.
1442         *
1443         * @param target The target object on which the value is set
1444         */
1445        @Override
1446        void setAnimatedValue(Object target) {
1447            float[] values = (float[]) getAnimatedValue();
1448            int numParameters = values.length;
1449            if (mJniSetter != 0) {
1450                switch (numParameters) {
1451                    case 1:
1452                        nCallFloatMethod(target, mJniSetter, values[0]);
1453                        break;
1454                    case 2:
1455                        nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1456                        break;
1457                    case 4:
1458                        nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1459                                values[2], values[3]);
1460                        break;
1461                    default: {
1462                        nCallMultipleFloatMethod(target, mJniSetter, values);
1463                        break;
1464                    }
1465                }
1466            }
1467        }
1468
1469        /**
1470         * Internal function (called from ObjectAnimator) to set up the setter and getter
1471         * prior to running the animation. No getter can be used for multiple parameters.
1472         *
1473         * @param target The object on which the setter exists.
1474         */
1475        @Override
1476        void setupSetterAndGetter(Object target) {
1477            setupSetter(target.getClass());
1478        }
1479
1480        @Override
1481        void setupSetter(Class targetClass) {
1482            if (mJniSetter != 0) {
1483                return;
1484            }
1485            synchronized(sJNISetterPropertyMap) {
1486                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1487                boolean wasInMap = false;
1488                if (propertyMap != null) {
1489                    wasInMap = propertyMap.containsKey(mPropertyName);
1490                    if (wasInMap) {
1491                        Long jniSetter = propertyMap.get(mPropertyName);
1492                        if (jniSetter != null) {
1493                            mJniSetter = jniSetter;
1494                        }
1495                    }
1496                }
1497                if (!wasInMap) {
1498                    String methodName = getMethodName("set", mPropertyName);
1499                    calculateValue(0f);
1500                    float[] values = (float[]) getAnimatedValue();
1501                    int numParams = values.length;
1502                    try {
1503                        mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1504                    } catch (NoSuchMethodError e) {
1505                        // try without the 'set' prefix
1506                        try {
1507                            mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
1508                                    numParams);
1509                        } catch (NoSuchMethodError e2) {
1510                            // just try reflection next
1511                        }
1512                    }
1513                    if (propertyMap == null) {
1514                        propertyMap = new HashMap<String, Long>();
1515                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1516                    }
1517                    propertyMap.put(mPropertyName, mJniSetter);
1518                }
1519           }
1520        }
1521    }
1522
1523    static class MultiIntValuesHolder extends PropertyValuesHolder {
1524        private long mJniSetter;
1525        private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
1526                new HashMap<Class, HashMap<String, Long>>();
1527
1528        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1529                TypeEvaluator evaluator, Object... values) {
1530            super(propertyName);
1531            setConverter(converter);
1532            setObjectValues(values);
1533            setEvaluator(evaluator);
1534        }
1535
1536        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1537                TypeEvaluator evaluator, Keyframes keyframes) {
1538            super(propertyName);
1539            setConverter(converter);
1540            mKeyframes = keyframes;
1541            setEvaluator(evaluator);
1542        }
1543
1544        /**
1545         * Internal function to set the value on the target object, using the setter set up
1546         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1547         * to handle turning the value calculated by ValueAnimator into a value set on the object
1548         * according to the name of the property.
1549         *
1550         * @param target The target object on which the value is set
1551         */
1552        @Override
1553        void setAnimatedValue(Object target) {
1554            int[] values = (int[]) getAnimatedValue();
1555            int numParameters = values.length;
1556            if (mJniSetter != 0) {
1557                switch (numParameters) {
1558                    case 1:
1559                        nCallIntMethod(target, mJniSetter, values[0]);
1560                        break;
1561                    case 2:
1562                        nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1563                        break;
1564                    case 4:
1565                        nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1566                                values[2], values[3]);
1567                        break;
1568                    default: {
1569                        nCallMultipleIntMethod(target, mJniSetter, values);
1570                        break;
1571                    }
1572                }
1573            }
1574        }
1575
1576        /**
1577         * Internal function (called from ObjectAnimator) to set up the setter and getter
1578         * prior to running the animation. No getter can be used for multiple parameters.
1579         *
1580         * @param target The object on which the setter exists.
1581         */
1582        @Override
1583        void setupSetterAndGetter(Object target) {
1584            setupSetter(target.getClass());
1585        }
1586
1587        @Override
1588        void setupSetter(Class targetClass) {
1589            if (mJniSetter != 0) {
1590                return;
1591            }
1592            synchronized(sJNISetterPropertyMap) {
1593                HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
1594                boolean wasInMap = false;
1595                if (propertyMap != null) {
1596                    wasInMap = propertyMap.containsKey(mPropertyName);
1597                    if (wasInMap) {
1598                        Long jniSetter = propertyMap.get(mPropertyName);
1599                        if (jniSetter != null) {
1600                            mJniSetter = jniSetter;
1601                        }
1602                    }
1603                }
1604                if (!wasInMap) {
1605                    String methodName = getMethodName("set", mPropertyName);
1606                    calculateValue(0f);
1607                    int[] values = (int[]) getAnimatedValue();
1608                    int numParams = values.length;
1609                    try {
1610                        mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1611                    } catch (NoSuchMethodError e) {
1612                        // try without the 'set' prefix
1613                        try {
1614                            mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
1615                                    numParams);
1616                        } catch (NoSuchMethodError e2) {
1617                            // couldn't find it.
1618                        }
1619                    }
1620                    if (propertyMap == null) {
1621                        propertyMap = new HashMap<String, Long>();
1622                        sJNISetterPropertyMap.put(targetClass, propertyMap);
1623                    }
1624                    propertyMap.put(mPropertyName, mJniSetter);
1625                }
1626            }
1627        }
1628    }
1629
1630    /**
1631     * Convert from PointF to float[] for multi-float setters along a Path.
1632     */
1633    private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1634        private float[] mCoordinates = new float[2];
1635
1636        public PointFToFloatArray() {
1637            super(PointF.class, float[].class);
1638        }
1639
1640        @Override
1641        public float[] convert(PointF value) {
1642            mCoordinates[0] = value.x;
1643            mCoordinates[1] = value.y;
1644            return mCoordinates;
1645        }
1646    };
1647
1648    /**
1649     * Convert from PointF to int[] for multi-int setters along a Path.
1650     */
1651    private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1652        private int[] mCoordinates = new int[2];
1653
1654        public PointFToIntArray() {
1655            super(PointF.class, int[].class);
1656        }
1657
1658        @Override
1659        public int[] convert(PointF value) {
1660            mCoordinates[0] = Math.round(value.x);
1661            mCoordinates[1] = Math.round(value.y);
1662            return mCoordinates;
1663        }
1664    };
1665
1666    /**
1667     * @hide
1668     */
1669    public static class PropertyValues {
1670        public String propertyName;
1671        public Class type;
1672        public Object startValue;
1673        public Object endValue;
1674        public DataSource dataSource = null;
1675        public interface DataSource {
1676            Object getValueAtFraction(float fraction);
1677        }
1678        public String toString() {
1679            return ("property name: " + propertyName + ", type: " + type + ", startValue: "
1680                    + startValue.toString() + ", endValue: " + endValue.toString());
1681        }
1682    }
1683
1684    native static private long nGetIntMethod(Class targetClass, String methodName);
1685    native static private long nGetFloatMethod(Class targetClass, String methodName);
1686    native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
1687            int numParams);
1688    native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
1689            int numParams);
1690    native static private void nCallIntMethod(Object target, long methodID, int arg);
1691    native static private void nCallFloatMethod(Object target, long methodID, float arg);
1692    native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
1693    native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
1694            int arg3, int arg4);
1695    native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
1696    native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
1697            float arg2);
1698    native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
1699            float arg2, float arg3, float arg4);
1700    native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
1701}
1702