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