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