PropertyValuesHolder.java revision c96c7b2e54965e30c8fb82295f1ca9f891ebd5e7
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 implement {@link TypeConverter#convertBack(Object)} 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    }
635
636    /**
637     * Sets the converter to convert from the values type to the setter's parameter type.
638     * @param converter The converter to use to convert values.
639     */
640    public void setConverter(TypeConverter converter) {
641        mConverter = converter;
642    }
643
644    /**
645     * Determine the setter or getter function using the JavaBeans convention of setFoo or
646     * getFoo for a property named 'foo'. This function figures out what the name of the
647     * function should be and uses reflection to find the Method with that name on the
648     * target object.
649     *
650     * @param targetClass The class to search for the method
651     * @param prefix "set" or "get", depending on whether we need a setter or getter.
652     * @param valueType The type of the parameter (in the case of a setter). This type
653     * is derived from the values set on this PropertyValuesHolder. This type is used as
654     * a first guess at the parameter type, but we check for methods with several different
655     * types to avoid problems with slight mis-matches between supplied values and actual
656     * value types used on the setter.
657     * @return Method the method associated with mPropertyName.
658     */
659    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
660        // TODO: faster implementation...
661        Method returnVal = null;
662        String methodName = getMethodName(prefix, mPropertyName);
663        Class args[] = null;
664        if (valueType == null) {
665            try {
666                returnVal = targetClass.getMethod(methodName, args);
667            } catch (NoSuchMethodException e) {
668                // Swallow the error, log it later
669            }
670        } else {
671            args = new Class[1];
672            Class typeVariants[];
673            if (valueType.equals(Float.class)) {
674                typeVariants = FLOAT_VARIANTS;
675            } else if (valueType.equals(Integer.class)) {
676                typeVariants = INTEGER_VARIANTS;
677            } else if (valueType.equals(Double.class)) {
678                typeVariants = DOUBLE_VARIANTS;
679            } else {
680                typeVariants = new Class[1];
681                typeVariants[0] = valueType;
682            }
683            for (Class typeVariant : typeVariants) {
684                args[0] = typeVariant;
685                try {
686                    returnVal = targetClass.getMethod(methodName, args);
687                    if (mConverter == null) {
688                        // change the value type to suit
689                        mValueType = typeVariant;
690                    }
691                    return returnVal;
692                } catch (NoSuchMethodException e) {
693                    // Swallow the error and keep trying other variants
694                }
695            }
696            // If we got here, then no appropriate function was found
697        }
698
699        if (returnVal == null) {
700            Log.w("PropertyValuesHolder", "Method " +
701                    getMethodName(prefix, mPropertyName) + "() with type " + valueType +
702                    " not found on target class " + targetClass);
703        }
704
705        return returnVal;
706    }
707
708
709    /**
710     * Returns the setter or getter requested. This utility function checks whether the
711     * requested method exists in the propertyMapMap cache. If not, it calls another
712     * utility function to request the Method from the targetClass directly.
713     * @param targetClass The Class on which the requested method should exist.
714     * @param propertyMapMap The cache of setters/getters derived so far.
715     * @param prefix "set" or "get", for the setter or getter.
716     * @param valueType The type of parameter passed into the method (null for getter).
717     * @return Method the method associated with mPropertyName.
718     */
719    private Method setupSetterOrGetter(Class targetClass,
720            HashMap<Class, HashMap<String, Method>> propertyMapMap,
721            String prefix, Class valueType) {
722        Method setterOrGetter = null;
723        try {
724            // Have to lock property map prior to reading it, to guard against
725            // another thread putting something in there after we've checked it
726            // but before we've added an entry to it
727            mPropertyMapLock.writeLock().lock();
728            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
729            if (propertyMap != null) {
730                setterOrGetter = propertyMap.get(mPropertyName);
731            }
732            if (setterOrGetter == null) {
733                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
734                if (propertyMap == null) {
735                    propertyMap = new HashMap<String, Method>();
736                    propertyMapMap.put(targetClass, propertyMap);
737                }
738                propertyMap.put(mPropertyName, setterOrGetter);
739            }
740        } finally {
741            mPropertyMapLock.writeLock().unlock();
742        }
743        return setterOrGetter;
744    }
745
746    /**
747     * Utility function to get the setter from targetClass
748     * @param targetClass The Class on which the requested method should exist.
749     */
750    void setupSetter(Class targetClass) {
751        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
752        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
753    }
754
755    /**
756     * Utility function to get the getter from targetClass
757     */
758    private void setupGetter(Class targetClass) {
759        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
760    }
761
762    /**
763     * Internal function (called from ObjectAnimator) to set up the setter and getter
764     * prior to running the animation. If the setter has not been manually set for this
765     * object, it will be derived automatically given the property name, target object, and
766     * types of values supplied. If no getter has been set, it will be supplied iff any of the
767     * supplied values was null. If there is a null value, then the getter (supplied or derived)
768     * will be called to set those null values to the current value of the property
769     * on the target object.
770     * @param target The object on which the setter (and possibly getter) exist.
771     */
772    void setupSetterAndGetter(Object target) {
773        if (mProperty != null) {
774            // check to make sure that mProperty is on the class of target
775            try {
776                Object testValue = null;
777                for (Keyframe kf : mKeyframeSet.mKeyframes) {
778                    if (!kf.hasValue()) {
779                        if (testValue == null) {
780                            testValue = convertBack(mProperty.get(target));
781                        }
782                        kf.setValue(testValue);
783                    }
784                }
785                return;
786            } catch (ClassCastException e) {
787                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
788                        ") on target object " + target + ". Trying reflection instead");
789                mProperty = null;
790            }
791        }
792        Class targetClass = target.getClass();
793        if (mSetter == null) {
794            setupSetter(targetClass);
795        }
796        for (Keyframe kf : mKeyframeSet.mKeyframes) {
797            if (!kf.hasValue()) {
798                if (mGetter == null) {
799                    setupGetter(targetClass);
800                    if (mGetter == null) {
801                        // Already logged the error - just return to avoid NPE
802                        return;
803                    }
804                }
805                try {
806                    Object value = convertBack(mGetter.invoke(target));
807                    kf.setValue(value);
808                } catch (InvocationTargetException e) {
809                    Log.e("PropertyValuesHolder", e.toString());
810                } catch (IllegalAccessException e) {
811                    Log.e("PropertyValuesHolder", e.toString());
812                }
813            }
814        }
815    }
816
817    private Object convertBack(Object value) {
818        if (mConverter != null) {
819            value = mConverter.convertBack(value);
820            if (value == null) {
821                throw new IllegalArgumentException("Converter "
822                        + mConverter.getClass().getName()
823                        + " must implement convertBack and not return null.");
824            }
825        }
826        return value;
827    }
828
829    /**
830     * Utility function to set the value stored in a particular Keyframe. The value used is
831     * whatever the value is for the property name specified in the keyframe on the target object.
832     *
833     * @param target The target object from which the current value should be extracted.
834     * @param kf The keyframe which holds the property name and value.
835     */
836    private void setupValue(Object target, Keyframe kf) {
837        if (mProperty != null) {
838            Object value = convertBack(mProperty.get(target));
839            kf.setValue(value);
840        }
841        try {
842            if (mGetter == null) {
843                Class targetClass = target.getClass();
844                setupGetter(targetClass);
845                if (mGetter == null) {
846                    // Already logged the error - just return to avoid NPE
847                    return;
848                }
849            }
850            Object value = convertBack(mGetter.invoke(target));
851            kf.setValue(value);
852        } catch (InvocationTargetException e) {
853            Log.e("PropertyValuesHolder", e.toString());
854        } catch (IllegalAccessException e) {
855            Log.e("PropertyValuesHolder", e.toString());
856        }
857    }
858
859    /**
860     * This function is called by ObjectAnimator when setting the start values for an animation.
861     * The start values are set according to the current values in the target object. The
862     * property whose value is extracted is whatever is specified by the propertyName of this
863     * PropertyValuesHolder object.
864     *
865     * @param target The object which holds the start values that should be set.
866     */
867    void setupStartValue(Object target) {
868        setupValue(target, mKeyframeSet.mKeyframes.get(0));
869    }
870
871    /**
872     * This function is called by ObjectAnimator when setting the end values for an animation.
873     * The end values are set according to the current values in the target object. The
874     * property whose value is extracted is whatever is specified by the propertyName of this
875     * PropertyValuesHolder object.
876     *
877     * @param target The object which holds the start values that should be set.
878     */
879    void setupEndValue(Object target) {
880        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
881    }
882
883    @Override
884    public PropertyValuesHolder clone() {
885        try {
886            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
887            newPVH.mPropertyName = mPropertyName;
888            newPVH.mProperty = mProperty;
889            newPVH.mKeyframeSet = mKeyframeSet.clone();
890            newPVH.mEvaluator = mEvaluator;
891            return newPVH;
892        } catch (CloneNotSupportedException e) {
893            // won't reach here
894            return null;
895        }
896    }
897
898    /**
899     * Internal function to set the value on the target object, using the setter set up
900     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
901     * to handle turning the value calculated by ValueAnimator into a value set on the object
902     * according to the name of the property.
903     * @param target The target object on which the value is set
904     */
905    void setAnimatedValue(Object target) {
906        if (mProperty != null) {
907            mProperty.set(target, getAnimatedValue());
908        }
909        if (mSetter != null) {
910            try {
911                mTmpValueArray[0] = getAnimatedValue();
912                mSetter.invoke(target, mTmpValueArray);
913            } catch (InvocationTargetException e) {
914                Log.e("PropertyValuesHolder", e.toString());
915            } catch (IllegalAccessException e) {
916                Log.e("PropertyValuesHolder", e.toString());
917            }
918        }
919    }
920
921    /**
922     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
923     * to calculate animated values.
924     */
925    void init() {
926        if (mEvaluator == null) {
927            // We already handle int and float automatically, but not their Object
928            // equivalents
929            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
930                    (mValueType == Float.class) ? sFloatEvaluator :
931                    null;
932        }
933        if (mEvaluator != null) {
934            // KeyframeSet knows how to evaluate the common types - only give it a custom
935            // evaluator if one has been set on this class
936            mKeyframeSet.setEvaluator(mEvaluator);
937        }
938    }
939
940    /**
941     * The TypeEvaluator will be automatically determined based on the type of values
942     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
943     * desired. This may be important in cases where either the type of the values supplied
944     * do not match the way that they should be interpolated between, or if the values
945     * are of a custom type or one not currently understood by the animation system. Currently,
946     * only values of type float and int (and their Object equivalents: Float
947     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
948     * @param evaluator
949     */
950    public void setEvaluator(TypeEvaluator evaluator) {
951        mEvaluator = evaluator;
952        mKeyframeSet.setEvaluator(evaluator);
953    }
954
955    /**
956     * Function used to calculate the value according to the evaluator set up for
957     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
958     *
959     * @param fraction The elapsed, interpolated fraction of the animation.
960     */
961    void calculateValue(float fraction) {
962        Object value = mKeyframeSet.getValue(fraction);
963        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
964    }
965
966    /**
967     * Sets the name of the property that will be animated. This name is used to derive
968     * a setter function that will be called to set animated values.
969     * For example, a property name of <code>foo</code> will result
970     * in a call to the function <code>setFoo()</code> on the target object. If either
971     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
972     * also be derived and called.
973     *
974     * <p>Note that the setter function derived from this property name
975     * must take the same parameter type as the
976     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
977     * the setter function will fail.</p>
978     *
979     * @param propertyName The name of the property being animated.
980     */
981    public void setPropertyName(String propertyName) {
982        mPropertyName = propertyName;
983    }
984
985    /**
986     * Sets the property that will be animated.
987     *
988     * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
989     * must exist on the target object specified in that ObjectAnimator.</p>
990     *
991     * @param property The property being animated.
992     */
993    public void setProperty(Property property) {
994        mProperty = property;
995    }
996
997    /**
998     * Gets the name of the property that will be animated. This name will be used to derive
999     * a setter function that will be called to set animated values.
1000     * For example, a property name of <code>foo</code> will result
1001     * in a call to the function <code>setFoo()</code> on the target object. If either
1002     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
1003     * also be derived and called.
1004     */
1005    public String getPropertyName() {
1006        return mPropertyName;
1007    }
1008
1009    /**
1010     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
1011     * most recently calculated in calculateValue().
1012     * @return
1013     */
1014    Object getAnimatedValue() {
1015        return mAnimatedValue;
1016    }
1017
1018    @Override
1019    public String toString() {
1020        return mPropertyName + ": " + mKeyframeSet.toString();
1021    }
1022
1023    /**
1024     * Utility method to derive a setter/getter method name from a property name, where the
1025     * prefix is typically "set" or "get" and the first letter of the property name is
1026     * capitalized.
1027     *
1028     * @param prefix The precursor to the method name, before the property name begins, typically
1029     * "set" or "get".
1030     * @param propertyName The name of the property that represents the bulk of the method name
1031     * after the prefix. The first letter of this word will be capitalized in the resulting
1032     * method name.
1033     * @return String the property name converted to a method name according to the conventions
1034     * specified above.
1035     */
1036    static String getMethodName(String prefix, String propertyName) {
1037        if (propertyName == null || propertyName.length() == 0) {
1038            // shouldn't get here
1039            return prefix;
1040        }
1041        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
1042        String theRest = propertyName.substring(1);
1043        return prefix + firstLetter + theRest;
1044    }
1045
1046    static class IntPropertyValuesHolder extends PropertyValuesHolder {
1047
1048        // Cache JNI functions to avoid looking them up twice
1049        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
1050                new HashMap<Class, HashMap<String, Integer>>();
1051        int mJniSetter;
1052        private IntProperty mIntProperty;
1053
1054        IntKeyframeSet mIntKeyframeSet;
1055        int mIntAnimatedValue;
1056
1057        public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
1058            super(propertyName);
1059            mValueType = int.class;
1060            mKeyframeSet = keyframeSet;
1061            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1062        }
1063
1064        public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
1065            super(property);
1066            mValueType = int.class;
1067            mKeyframeSet = keyframeSet;
1068            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1069            if (property instanceof  IntProperty) {
1070                mIntProperty = (IntProperty) mProperty;
1071            }
1072        }
1073
1074        public IntPropertyValuesHolder(String propertyName, int... values) {
1075            super(propertyName);
1076            setIntValues(values);
1077        }
1078
1079        public IntPropertyValuesHolder(Property property, int... values) {
1080            super(property);
1081            setIntValues(values);
1082            if (property instanceof  IntProperty) {
1083                mIntProperty = (IntProperty) mProperty;
1084            }
1085        }
1086
1087        @Override
1088        public void setIntValues(int... values) {
1089            super.setIntValues(values);
1090            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
1091        }
1092
1093        @Override
1094        void calculateValue(float fraction) {
1095            mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
1096        }
1097
1098        @Override
1099        Object getAnimatedValue() {
1100            return mIntAnimatedValue;
1101        }
1102
1103        @Override
1104        public IntPropertyValuesHolder clone() {
1105            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
1106            newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
1107            return newPVH;
1108        }
1109
1110        /**
1111         * Internal function to set the value on the target object, using the setter set up
1112         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1113         * to handle turning the value calculated by ValueAnimator into a value set on the object
1114         * according to the name of the property.
1115         * @param target The target object on which the value is set
1116         */
1117        @Override
1118        void setAnimatedValue(Object target) {
1119            if (mIntProperty != null) {
1120                mIntProperty.setValue(target, mIntAnimatedValue);
1121                return;
1122            }
1123            if (mProperty != null) {
1124                mProperty.set(target, mIntAnimatedValue);
1125                return;
1126            }
1127            if (mJniSetter != 0) {
1128                nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
1129                return;
1130            }
1131            if (mSetter != null) {
1132                try {
1133                    mTmpValueArray[0] = mIntAnimatedValue;
1134                    mSetter.invoke(target, mTmpValueArray);
1135                } catch (InvocationTargetException e) {
1136                    Log.e("PropertyValuesHolder", e.toString());
1137                } catch (IllegalAccessException e) {
1138                    Log.e("PropertyValuesHolder", e.toString());
1139                }
1140            }
1141        }
1142
1143        @Override
1144        void setupSetter(Class targetClass) {
1145            if (mProperty != null) {
1146                return;
1147            }
1148            // Check new static hashmap<propName, int> for setter method
1149            try {
1150                mPropertyMapLock.writeLock().lock();
1151                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
1152                if (propertyMap != null) {
1153                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
1154                    if (mJniSetterInteger != null) {
1155                        mJniSetter = mJniSetterInteger;
1156                    }
1157                }
1158                if (mJniSetter == 0) {
1159                    String methodName = getMethodName("set", mPropertyName);
1160                    mJniSetter = nGetIntMethod(targetClass, methodName);
1161                    if (mJniSetter != 0) {
1162                        if (propertyMap == null) {
1163                            propertyMap = new HashMap<String, Integer>();
1164                            sJNISetterPropertyMap.put(targetClass, propertyMap);
1165                        }
1166                        propertyMap.put(mPropertyName, mJniSetter);
1167                    }
1168                }
1169            } catch (NoSuchMethodError e) {
1170                // Couldn't find it via JNI - try reflection next. Probably means the method
1171                // doesn't exist, or the type is wrong. An error will be logged later if
1172                // reflection fails as well.
1173            } finally {
1174                mPropertyMapLock.writeLock().unlock();
1175            }
1176            if (mJniSetter == 0) {
1177                // Couldn't find method through fast JNI approach - just use reflection
1178                super.setupSetter(targetClass);
1179            }
1180        }
1181    }
1182
1183    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
1184
1185        // Cache JNI functions to avoid looking them up twice
1186        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
1187                new HashMap<Class, HashMap<String, Integer>>();
1188        int mJniSetter;
1189        private FloatProperty mFloatProperty;
1190
1191        FloatKeyframeSet mFloatKeyframeSet;
1192        float mFloatAnimatedValue;
1193
1194        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
1195            super(propertyName);
1196            mValueType = float.class;
1197            mKeyframeSet = keyframeSet;
1198            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1199        }
1200
1201        public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
1202            super(property);
1203            mValueType = float.class;
1204            mKeyframeSet = keyframeSet;
1205            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1206            if (property instanceof FloatProperty) {
1207                mFloatProperty = (FloatProperty) mProperty;
1208            }
1209        }
1210
1211        public FloatPropertyValuesHolder(String propertyName, float... values) {
1212            super(propertyName);
1213            setFloatValues(values);
1214        }
1215
1216        public FloatPropertyValuesHolder(Property property, float... values) {
1217            super(property);
1218            setFloatValues(values);
1219            if (property instanceof  FloatProperty) {
1220                mFloatProperty = (FloatProperty) mProperty;
1221            }
1222        }
1223
1224        @Override
1225        public void setFloatValues(float... values) {
1226            super.setFloatValues(values);
1227            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
1228        }
1229
1230        @Override
1231        void calculateValue(float fraction) {
1232            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
1233        }
1234
1235        @Override
1236        Object getAnimatedValue() {
1237            return mFloatAnimatedValue;
1238        }
1239
1240        @Override
1241        public FloatPropertyValuesHolder clone() {
1242            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1243            newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
1244            return newPVH;
1245        }
1246
1247        /**
1248         * Internal function to set the value on the target object, using the setter set up
1249         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1250         * to handle turning the value calculated by ValueAnimator into a value set on the object
1251         * according to the name of the property.
1252         * @param target The target object on which the value is set
1253         */
1254        @Override
1255        void setAnimatedValue(Object target) {
1256            if (mFloatProperty != null) {
1257                mFloatProperty.setValue(target, mFloatAnimatedValue);
1258                return;
1259            }
1260            if (mProperty != null) {
1261                mProperty.set(target, mFloatAnimatedValue);
1262                return;
1263            }
1264            if (mJniSetter != 0) {
1265                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1266                return;
1267            }
1268            if (mSetter != null) {
1269                try {
1270                    mTmpValueArray[0] = mFloatAnimatedValue;
1271                    mSetter.invoke(target, mTmpValueArray);
1272                } catch (InvocationTargetException e) {
1273                    Log.e("PropertyValuesHolder", e.toString());
1274                } catch (IllegalAccessException e) {
1275                    Log.e("PropertyValuesHolder", e.toString());
1276                }
1277            }
1278        }
1279
1280        @Override
1281        void setupSetter(Class targetClass) {
1282            if (mProperty != null) {
1283                return;
1284            }
1285            // Check new static hashmap<propName, int> for setter method
1286            try {
1287                mPropertyMapLock.writeLock().lock();
1288                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
1289                if (propertyMap != null) {
1290                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
1291                    if (mJniSetterInteger != null) {
1292                        mJniSetter = mJniSetterInteger;
1293                    }
1294                }
1295                if (mJniSetter == 0) {
1296                    String methodName = getMethodName("set", mPropertyName);
1297                    mJniSetter = nGetFloatMethod(targetClass, methodName);
1298                    if (mJniSetter != 0) {
1299                        if (propertyMap == null) {
1300                            propertyMap = new HashMap<String, Integer>();
1301                            sJNISetterPropertyMap.put(targetClass, propertyMap);
1302                        }
1303                        propertyMap.put(mPropertyName, mJniSetter);
1304                    }
1305                }
1306            } catch (NoSuchMethodError e) {
1307                // Couldn't find it via JNI - try reflection next. Probably means the method
1308                // doesn't exist, or the type is wrong. An error will be logged later if
1309                // reflection fails as well.
1310            } finally {
1311                mPropertyMapLock.writeLock().unlock();
1312            }
1313            if (mJniSetter == 0) {
1314                // Couldn't find method through fast JNI approach - just use reflection
1315                super.setupSetter(targetClass);
1316            }
1317        }
1318
1319    }
1320
1321    static class MultiFloatValuesHolder extends PropertyValuesHolder {
1322        private int mJniSetter;
1323        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
1324                new HashMap<Class, HashMap<String, Integer>>();
1325
1326        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1327                TypeEvaluator evaluator, Object... values) {
1328            super(propertyName);
1329            setConverter(converter);
1330            setObjectValues(values);
1331            setEvaluator(evaluator);
1332        }
1333
1334        public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
1335                TypeEvaluator evaluator, KeyframeSet keyframeSet) {
1336            super(propertyName);
1337            setConverter(converter);
1338            mKeyframeSet = keyframeSet;
1339            setEvaluator(evaluator);
1340        }
1341
1342        /**
1343         * Internal function to set the value on the target object, using the setter set up
1344         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1345         * to handle turning the value calculated by ValueAnimator into a value set on the object
1346         * according to the name of the property.
1347         *
1348         * @param target The target object on which the value is set
1349         */
1350        @Override
1351        void setAnimatedValue(Object target) {
1352            float[] values = (float[]) getAnimatedValue();
1353            int numParameters = values.length;
1354            if (mJniSetter != 0) {
1355                switch (numParameters) {
1356                    case 1:
1357                        nCallFloatMethod(target, mJniSetter, values[0]);
1358                        break;
1359                    case 2:
1360                        nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
1361                        break;
1362                    case 4:
1363                        nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
1364                                values[2], values[3]);
1365                        break;
1366                    default: {
1367                        nCallMultipleFloatMethod(target, mJniSetter, values);
1368                        break;
1369                    }
1370                }
1371            }
1372        }
1373
1374        /**
1375         * Internal function (called from ObjectAnimator) to set up the setter and getter
1376         * prior to running the animation. No getter can be used for multiple parameters.
1377         *
1378         * @param target The object on which the setter exists.
1379         */
1380        @Override
1381        void setupSetterAndGetter(Object target) {
1382            setupSetter(target.getClass());
1383        }
1384
1385        @Override
1386        void setupSetter(Class targetClass) {
1387            if (mJniSetter != 0) {
1388                return;
1389            }
1390            try {
1391                mPropertyMapLock.writeLock().lock();
1392                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
1393                if (propertyMap != null) {
1394                    Integer jniSetterInteger = propertyMap.get(mPropertyName);
1395                    if (jniSetterInteger != null) {
1396                        mJniSetter = jniSetterInteger;
1397                    }
1398                }
1399                if (mJniSetter == 0) {
1400                    String methodName = getMethodName("set", mPropertyName);
1401                    calculateValue(0f);
1402                    float[] values = (float[]) getAnimatedValue();
1403                    int numParams = values.length;
1404                    try {
1405                        mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
1406                    } catch (NoSuchMethodError e) {
1407                        // try without the 'set' prefix
1408                        mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, numParams);
1409                    }
1410                    if (mJniSetter != 0) {
1411                        if (propertyMap == null) {
1412                            propertyMap = new HashMap<String, Integer>();
1413                            sJNISetterPropertyMap.put(targetClass, propertyMap);
1414                        }
1415                        propertyMap.put(mPropertyName, mJniSetter);
1416                    }
1417                }
1418            } finally {
1419                mPropertyMapLock.writeLock().unlock();
1420            }
1421        }
1422    }
1423
1424    static class MultiIntValuesHolder extends PropertyValuesHolder {
1425        private int mJniSetter;
1426        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
1427                new HashMap<Class, HashMap<String, Integer>>();
1428
1429        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1430                TypeEvaluator evaluator, Object... values) {
1431            super(propertyName);
1432            setConverter(converter);
1433            setObjectValues(values);
1434            setEvaluator(evaluator);
1435        }
1436
1437        public MultiIntValuesHolder(String propertyName, TypeConverter converter,
1438                TypeEvaluator evaluator, KeyframeSet keyframeSet) {
1439            super(propertyName);
1440            setConverter(converter);
1441            mKeyframeSet = keyframeSet;
1442            setEvaluator(evaluator);
1443        }
1444
1445        /**
1446         * Internal function to set the value on the target object, using the setter set up
1447         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1448         * to handle turning the value calculated by ValueAnimator into a value set on the object
1449         * according to the name of the property.
1450         *
1451         * @param target The target object on which the value is set
1452         */
1453        @Override
1454        void setAnimatedValue(Object target) {
1455            int[] values = (int[]) getAnimatedValue();
1456            int numParameters = values.length;
1457            if (mJniSetter != 0) {
1458                switch (numParameters) {
1459                    case 1:
1460                        nCallIntMethod(target, mJniSetter, values[0]);
1461                        break;
1462                    case 2:
1463                        nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
1464                        break;
1465                    case 4:
1466                        nCallFourIntMethod(target, mJniSetter, values[0], values[1],
1467                                values[2], values[3]);
1468                        break;
1469                    default: {
1470                        nCallMultipleIntMethod(target, mJniSetter, values);
1471                        break;
1472                    }
1473                }
1474            }
1475        }
1476
1477        /**
1478         * Internal function (called from ObjectAnimator) to set up the setter and getter
1479         * prior to running the animation. No getter can be used for multiple parameters.
1480         *
1481         * @param target The object on which the setter exists.
1482         */
1483        @Override
1484        void setupSetterAndGetter(Object target) {
1485            setupSetter(target.getClass());
1486        }
1487
1488        @Override
1489        void setupSetter(Class targetClass) {
1490            if (mJniSetter != 0) {
1491                return;
1492            }
1493            try {
1494                mPropertyMapLock.writeLock().lock();
1495                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
1496                if (propertyMap != null) {
1497                    Integer jniSetterInteger = propertyMap.get(mPropertyName);
1498                    if (jniSetterInteger != null) {
1499                        mJniSetter = jniSetterInteger;
1500                    }
1501                }
1502                if (mJniSetter == 0) {
1503                    String methodName = getMethodName("set", mPropertyName);
1504                    calculateValue(0f);
1505                    int[] values = (int[]) getAnimatedValue();
1506                    int numParams = values.length;
1507                    try {
1508                        mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
1509                    } catch (NoSuchMethodError e) {
1510                        // try without the 'set' prefix
1511                        mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, numParams);
1512                    }
1513                    if (mJniSetter != 0) {
1514                        if (propertyMap == null) {
1515                            propertyMap = new HashMap<String, Integer>();
1516                            sJNISetterPropertyMap.put(targetClass, propertyMap);
1517                        }
1518                        propertyMap.put(mPropertyName, mJniSetter);
1519                    }
1520                }
1521            } finally {
1522                mPropertyMapLock.writeLock().unlock();
1523            }
1524        }
1525    }
1526
1527    /* Path interpolation relies on approximating the Path as a series of line segments.
1528       The line segments are recursively divided until there is less than 1/2 pixel error
1529       between the lines and the curve. Each point of the line segment is converted
1530       to a Keyframe and a linear interpolation between Keyframes creates a good approximation
1531       of the curve.
1532
1533       The fraction for each Keyframe is the length along the Path to the point, divided by
1534       the total Path length. Two points may have the same fraction in the case of a move
1535       command causing a disjoint Path.
1536
1537       The value for each Keyframe is either the point as a PointF or one of the x or y
1538       coordinates as an int or float. In the latter case, two Keyframes are generated for
1539       each point that have the same fraction. */
1540
1541    /**
1542     * Returns separate Keyframes arrays for the x and y coordinates along a Path. If
1543     * isInt is true, the Keyframes will be IntKeyframes, otherwise they will be FloatKeyframes.
1544     * The element at index 0 are the x coordinate Keyframes and element at index 1 are the
1545     * y coordinate Keyframes. The returned values can be linearly interpolated and get less
1546     * than 1/2 pixel error.
1547     */
1548    static Keyframe[][] createKeyframes(Path path, boolean isInt) {
1549        if (path == null || path.isEmpty()) {
1550            throw new IllegalArgumentException("The path must not be null or empty");
1551        }
1552        float[] pointComponents = path.approximate(0.5f);
1553
1554        int numPoints = pointComponents.length / 3;
1555
1556        Keyframe[][] keyframes = new Keyframe[2][];
1557        keyframes[0] = new Keyframe[numPoints];
1558        keyframes[1] = new Keyframe[numPoints];
1559        int componentIndex = 0;
1560        for (int i = 0; i < numPoints; i++) {
1561            float fraction = pointComponents[componentIndex++];
1562            float x = pointComponents[componentIndex++];
1563            float y = pointComponents[componentIndex++];
1564            if (isInt) {
1565                keyframes[0][i] = Keyframe.ofInt(fraction, Math.round(x));
1566                keyframes[1][i] = Keyframe.ofInt(fraction, Math.round(y));
1567            } else {
1568                keyframes[0][i] = Keyframe.ofFloat(fraction, x);
1569                keyframes[1][i] = Keyframe.ofFloat(fraction, y);
1570            }
1571        }
1572        return keyframes;
1573    }
1574
1575    /**
1576     * Returns PointF Keyframes for a Path. The resulting points can be linearly interpolated
1577     * with less than 1/2 pixel in error.
1578     */
1579    private static Keyframe[] createKeyframes(Path path) {
1580        if (path == null || path.isEmpty()) {
1581            throw new IllegalArgumentException("The path must not be null or empty");
1582        }
1583        float[] pointComponents = path.approximate(0.5f);
1584
1585        int numPoints = pointComponents.length / 3;
1586
1587        Keyframe[] keyframes = new Keyframe[numPoints];
1588        int componentIndex = 0;
1589        for (int i = 0; i < numPoints; i++) {
1590            float fraction = pointComponents[componentIndex++];
1591            float x = pointComponents[componentIndex++];
1592            float y = pointComponents[componentIndex++];
1593            keyframes[i] = Keyframe.ofObject(fraction, new PointF(x, y));
1594        }
1595        return keyframes;
1596    }
1597
1598    /**
1599     * Convert from PointF to float[] for multi-float setters along a Path.
1600     */
1601    private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
1602        private float[] mCoordinates = new float[2];
1603
1604        public PointFToFloatArray() {
1605            super(PointF.class, float[].class);
1606        }
1607
1608        @Override
1609        public float[] convert(PointF value) {
1610            mCoordinates[0] = value.x;
1611            mCoordinates[1] = value.y;
1612            return mCoordinates;
1613        }
1614    };
1615
1616    /**
1617     * Convert from PointF to int[] for multi-int setters along a Path.
1618     */
1619    private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
1620        private int[] mCoordinates = new int[2];
1621
1622        public PointFToIntArray() {
1623            super(PointF.class, int[].class);
1624        }
1625
1626        @Override
1627        public int[] convert(PointF value) {
1628            mCoordinates[0] = Math.round(value.x);
1629            mCoordinates[1] = Math.round(value.y);
1630            return mCoordinates;
1631        }
1632    };
1633
1634    native static private int nGetIntMethod(Class targetClass, String methodName);
1635    native static private int nGetFloatMethod(Class targetClass, String methodName);
1636    native static private int nGetMultipleIntMethod(Class targetClass, String methodName,
1637            int numParams);
1638    native static private int nGetMultipleFloatMethod(Class targetClass, String methodName,
1639            int numParams);
1640    native static private void nCallIntMethod(Object target, int methodID, int arg);
1641    native static private void nCallFloatMethod(Object target, int methodID, float arg);
1642    native static private void nCallTwoIntMethod(Object target, int methodID, int arg1, int arg2);
1643    native static private void nCallFourIntMethod(Object target, int methodID, int arg1, int arg2,
1644            int arg3, int arg4);
1645    native static private void nCallMultipleIntMethod(Object target, int methodID, int[] args);
1646    native static private void nCallTwoFloatMethod(Object target, int methodID, float arg1,
1647            float arg2);
1648    native static private void nCallFourFloatMethod(Object target, int methodID, float arg1,
1649            float arg2, float arg3, float arg4);
1650    native static private void nCallMultipleFloatMethod(Object target, int methodID, float[] args);
1651}
1652