PropertyValuesHolder.java revision 16d2c9cc6bd67131d9921fbc14a69d88f48f48ca
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 float values.
175     * @param propertyName The name of the property being animated.
176     * @param values The values that the named property will animate between.
177     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
178     */
179    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
180        return new FloatPropertyValuesHolder(propertyName, values);
181    }
182
183    /**
184     * Constructs and returns a PropertyValuesHolder with a given property and
185     * set of float values.
186     * @param property The property being animated. Should not be null.
187     * @param values The values that the property will animate between.
188     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
189     */
190    public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
191        return new FloatPropertyValuesHolder(property, values);
192    }
193
194    /**
195     * Constructs and returns a PropertyValuesHolder with a given property name and
196     * set of Object values. This variant also takes a TypeEvaluator because the system
197     * cannot automatically interpolate between objects of unknown type.
198     *
199     * @param propertyName The name of the property being animated.
200     * @param evaluator A TypeEvaluator that will be called on each animation frame to
201     * provide the necessary interpolation between the Object values to derive the animated
202     * value.
203     * @param values The values that the named property will animate between.
204     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
205     */
206    public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
207            Object... values) {
208        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
209        pvh.setObjectValues(values);
210        pvh.setEvaluator(evaluator);
211        return pvh;
212    }
213
214    /**
215     * Constructs and returns a PropertyValuesHolder with a given property and
216     * set of Object values. This variant also takes a TypeEvaluator because the system
217     * cannot automatically interpolate between objects of unknown type.
218     *
219     * @param property The property being animated. Should not be null.
220     * @param evaluator A TypeEvaluator that will be called on each animation frame to
221     * provide the necessary interpolation between the Object values to derive the animated
222     * value.
223     * @param values The values that the property will animate between.
224     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
225     */
226    public static <V> PropertyValuesHolder ofObject(Property property,
227            TypeEvaluator<V> evaluator, V... values) {
228        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
229        pvh.setObjectValues(values);
230        pvh.setEvaluator(evaluator);
231        return pvh;
232    }
233
234    /**
235     * Constructs and returns a PropertyValuesHolder with a given property and
236     * set of Object values. This variant also takes a TypeEvaluator because the system
237     * cannot automatically interpolate between objects of unknown type. This variant also
238     * takes a <code>TypeConverter</code> to convert from animated values to the type
239     * of the property. If only one value is supplied, the <code>TypeConverter</code>
240     * must implement {@link TypeConverter#convertBack(Object)} to retrieve the current
241     * value.
242     *
243     * @param property The property being animated. Should not be null.
244     * @param converter Converts the animated object to the Property type.
245     * @param evaluator A TypeEvaluator that will be called on each animation frame to
246     * provide the necessary interpolation between the Object values to derive the animated
247     * value.
248     * @param values The values that the property will animate between.
249     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
250     * @see #setConverter(TypeConverter)
251     * @see TypeConverter
252     */
253    public static <T, V> PropertyValuesHolder ofObject(Property<?, V> property,
254            TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values) {
255        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
256        pvh.setConverter(converter);
257        pvh.setObjectValues(values);
258        pvh.setEvaluator(evaluator);
259        return pvh;
260    }
261
262    /**
263     * Constructs and returns a PropertyValuesHolder object with the specified property name and set
264     * of values. These values can be of any type, but the type should be consistent so that
265     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
266     * the common type.
267     * <p>If there is only one value, it is assumed to be the end value of an animation,
268     * and an initial value will be derived, if possible, by calling a getter function
269     * on the object. Also, if any value is null, the value will be filled in when the animation
270     * starts in the same way. This mechanism of automatically getting null values only works
271     * if the PropertyValuesHolder object is used in conjunction
272     * {@link ObjectAnimator}, and with a getter function
273     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
274     * no way of determining what the value should be.
275     * @param propertyName The name of the property associated with this set of values. This
276     * can be the actual property name to be used when using a ObjectAnimator object, or
277     * just a name used to get animated values, such as if this object is used with an
278     * ValueAnimator object.
279     * @param values The set of values to animate between.
280     */
281    public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
282        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
283        if (keyframeSet instanceof IntKeyframeSet) {
284            return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
285        } else if (keyframeSet instanceof FloatKeyframeSet) {
286            return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
287        }
288        else {
289            PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
290            pvh.mKeyframeSet = keyframeSet;
291            pvh.mValueType = ((Keyframe)values[0]).getType();
292            return pvh;
293        }
294    }
295
296    /**
297     * Constructs and returns a PropertyValuesHolder object with the specified property and set
298     * of values. These values can be of any type, but the type should be consistent so that
299     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
300     * the common type.
301     * <p>If there is only one value, it is assumed to be the end value of an animation,
302     * and an initial value will be derived, if possible, by calling the property's
303     * {@link android.util.Property#get(Object)} function.
304     * Also, if any value is null, the value will be filled in when the animation
305     * starts in the same way. This mechanism of automatically getting null values only works
306     * if the PropertyValuesHolder object is used in conjunction with
307     * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
308     * no way of determining what the value should be.
309     * @param property The property associated with this set of values. Should not be null.
310     * @param values The set of values to animate between.
311     */
312    public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
313        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
314        if (keyframeSet instanceof IntKeyframeSet) {
315            return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
316        } else if (keyframeSet instanceof FloatKeyframeSet) {
317            return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
318        }
319        else {
320            PropertyValuesHolder pvh = new PropertyValuesHolder(property);
321            pvh.mKeyframeSet = keyframeSet;
322            pvh.mValueType = ((Keyframe)values[0]).getType();
323            return pvh;
324        }
325    }
326
327    /**
328     * Set the animated values for this object to this set of ints.
329     * If there is only one value, it is assumed to be the end value of an animation,
330     * and an initial value will be derived, if possible, by calling a getter function
331     * on the object. Also, if any value is null, the value will be filled in when the animation
332     * starts in the same way. This mechanism of automatically getting null values only works
333     * if the PropertyValuesHolder object is used in conjunction
334     * {@link ObjectAnimator}, and with a getter function
335     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
336     * no way of determining what the value should be.
337     *
338     * @param values One or more values that the animation will animate between.
339     */
340    public void setIntValues(int... values) {
341        mValueType = int.class;
342        mKeyframeSet = KeyframeSet.ofInt(values);
343    }
344
345    /**
346     * Set the animated values for this object to this set of floats.
347     * If there is only one value, it is assumed to be the end value of an animation,
348     * and an initial value will be derived, if possible, by calling a getter function
349     * on the object. Also, if any value is null, the value will be filled in when the animation
350     * starts in the same way. This mechanism of automatically getting null values only works
351     * if the PropertyValuesHolder object is used in conjunction
352     * {@link ObjectAnimator}, and with a getter function
353     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
354     * no way of determining what the value should be.
355     *
356     * @param values One or more values that the animation will animate between.
357     */
358    public void setFloatValues(float... values) {
359        mValueType = float.class;
360        mKeyframeSet = KeyframeSet.ofFloat(values);
361    }
362
363    /**
364     * Set the animated values for this object to this set of Keyframes.
365     *
366     * @param values One or more values that the animation will animate between.
367     */
368    public void setKeyframes(Keyframe... values) {
369        int numKeyframes = values.length;
370        Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
371        mValueType = ((Keyframe)values[0]).getType();
372        for (int i = 0; i < numKeyframes; ++i) {
373            keyframes[i] = (Keyframe)values[i];
374        }
375        mKeyframeSet = new KeyframeSet(keyframes);
376    }
377
378    /**
379     * Set the animated values for this object to this set of Objects.
380     * If there is only one value, it is assumed to be the end value of an animation,
381     * and an initial value will be derived, if possible, by calling a getter function
382     * on the object. Also, if any value is null, the value will be filled in when the animation
383     * starts in the same way. This mechanism of automatically getting null values only works
384     * if the PropertyValuesHolder object is used in conjunction
385     * {@link ObjectAnimator}, and with a getter function
386     * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
387     * no way of determining what the value should be.
388     *
389     * @param values One or more values that the animation will animate between.
390     */
391    public void setObjectValues(Object... values) {
392        mValueType = values[0].getClass();
393        mKeyframeSet = KeyframeSet.ofObject(values);
394    }
395
396    /**
397     * Sets the converter to convert from the values type to the setter's parameter type.
398     * @param converter The converter to use to convert values.
399     */
400    public void setConverter(TypeConverter converter) {
401        mConverter = converter;
402    }
403
404    /**
405     * Determine the setter or getter function using the JavaBeans convention of setFoo or
406     * getFoo for a property named 'foo'. This function figures out what the name of the
407     * function should be and uses reflection to find the Method with that name on the
408     * target object.
409     *
410     * @param targetClass The class to search for the method
411     * @param prefix "set" or "get", depending on whether we need a setter or getter.
412     * @param valueType The type of the parameter (in the case of a setter). This type
413     * is derived from the values set on this PropertyValuesHolder. This type is used as
414     * a first guess at the parameter type, but we check for methods with several different
415     * types to avoid problems with slight mis-matches between supplied values and actual
416     * value types used on the setter.
417     * @return Method the method associated with mPropertyName.
418     */
419    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
420        // TODO: faster implementation...
421        Method returnVal = null;
422        String methodName = getMethodName(prefix, mPropertyName);
423        Class args[] = null;
424        if (valueType == null) {
425            try {
426                returnVal = targetClass.getMethod(methodName, args);
427            } catch (NoSuchMethodException e) {
428                // Swallow the error, log it later
429            }
430        } else {
431            args = new Class[1];
432            Class typeVariants[];
433            if (valueType.equals(Float.class)) {
434                typeVariants = FLOAT_VARIANTS;
435            } else if (valueType.equals(Integer.class)) {
436                typeVariants = INTEGER_VARIANTS;
437            } else if (valueType.equals(Double.class)) {
438                typeVariants = DOUBLE_VARIANTS;
439            } else {
440                typeVariants = new Class[1];
441                typeVariants[0] = valueType;
442            }
443            for (Class typeVariant : typeVariants) {
444                args[0] = typeVariant;
445                try {
446                    returnVal = targetClass.getMethod(methodName, args);
447                    if (mConverter == null) {
448                        // change the value type to suit
449                        mValueType = typeVariant;
450                    }
451                    return returnVal;
452                } catch (NoSuchMethodException e) {
453                    // Swallow the error and keep trying other variants
454                }
455            }
456            // If we got here, then no appropriate function was found
457        }
458
459        if (returnVal == null) {
460            Log.w("PropertyValuesHolder", "Method " +
461                    getMethodName(prefix, mPropertyName) + "() with type " + valueType +
462                    " not found on target class " + targetClass);
463        }
464
465        return returnVal;
466    }
467
468
469    /**
470     * Returns the setter or getter requested. This utility function checks whether the
471     * requested method exists in the propertyMapMap cache. If not, it calls another
472     * utility function to request the Method from the targetClass directly.
473     * @param targetClass The Class on which the requested method should exist.
474     * @param propertyMapMap The cache of setters/getters derived so far.
475     * @param prefix "set" or "get", for the setter or getter.
476     * @param valueType The type of parameter passed into the method (null for getter).
477     * @return Method the method associated with mPropertyName.
478     */
479    private Method setupSetterOrGetter(Class targetClass,
480            HashMap<Class, HashMap<String, Method>> propertyMapMap,
481            String prefix, Class valueType) {
482        Method setterOrGetter = null;
483        try {
484            // Have to lock property map prior to reading it, to guard against
485            // another thread putting something in there after we've checked it
486            // but before we've added an entry to it
487            mPropertyMapLock.writeLock().lock();
488            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
489            if (propertyMap != null) {
490                setterOrGetter = propertyMap.get(mPropertyName);
491            }
492            if (setterOrGetter == null) {
493                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
494                if (propertyMap == null) {
495                    propertyMap = new HashMap<String, Method>();
496                    propertyMapMap.put(targetClass, propertyMap);
497                }
498                propertyMap.put(mPropertyName, setterOrGetter);
499            }
500        } finally {
501            mPropertyMapLock.writeLock().unlock();
502        }
503        return setterOrGetter;
504    }
505
506    /**
507     * Utility function to get the setter from targetClass
508     * @param targetClass The Class on which the requested method should exist.
509     */
510    void setupSetter(Class targetClass) {
511        Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
512        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
513    }
514
515    /**
516     * Utility function to get the getter from targetClass
517     */
518    private void setupGetter(Class targetClass) {
519        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
520    }
521
522    /**
523     * Internal function (called from ObjectAnimator) to set up the setter and getter
524     * prior to running the animation. If the setter has not been manually set for this
525     * object, it will be derived automatically given the property name, target object, and
526     * types of values supplied. If no getter has been set, it will be supplied iff any of the
527     * supplied values was null. If there is a null value, then the getter (supplied or derived)
528     * will be called to set those null values to the current value of the property
529     * on the target object.
530     * @param target The object on which the setter (and possibly getter) exist.
531     */
532    void setupSetterAndGetter(Object target) {
533        if (mProperty != null) {
534            // check to make sure that mProperty is on the class of target
535            try {
536                Object testValue = null;
537                for (Keyframe kf : mKeyframeSet.mKeyframes) {
538                    if (!kf.hasValue()) {
539                        if (testValue == null) {
540                            testValue = convertBack(mProperty.get(target));
541                        }
542                        kf.setValue(testValue);
543                    }
544                }
545                return;
546            } catch (ClassCastException e) {
547                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
548                        ") on target object " + target + ". Trying reflection instead");
549                mProperty = null;
550            }
551        }
552        Class targetClass = target.getClass();
553        if (mSetter == null) {
554            setupSetter(targetClass);
555        }
556        for (Keyframe kf : mKeyframeSet.mKeyframes) {
557            if (!kf.hasValue()) {
558                if (mGetter == null) {
559                    setupGetter(targetClass);
560                    if (mGetter == null) {
561                        // Already logged the error - just return to avoid NPE
562                        return;
563                    }
564                }
565                try {
566                    Object value = convertBack(mGetter.invoke(target));
567                    kf.setValue(value);
568                } catch (InvocationTargetException e) {
569                    Log.e("PropertyValuesHolder", e.toString());
570                } catch (IllegalAccessException e) {
571                    Log.e("PropertyValuesHolder", e.toString());
572                }
573            }
574        }
575    }
576
577    private Object convertBack(Object value) {
578        if (mConverter != null) {
579            value = mConverter.convertBack(value);
580            if (value == null) {
581                throw new IllegalArgumentException("Converter "
582                        + mConverter.getClass().getName()
583                        + " must implement convertBack and not return null.");
584            }
585        }
586        return value;
587    }
588
589    /**
590     * Utility function to set the value stored in a particular Keyframe. The value used is
591     * whatever the value is for the property name specified in the keyframe on the target object.
592     *
593     * @param target The target object from which the current value should be extracted.
594     * @param kf The keyframe which holds the property name and value.
595     */
596    private void setupValue(Object target, Keyframe kf) {
597        if (mProperty != null) {
598            Object value = convertBack(mProperty.get(target));
599            kf.setValue(value);
600        }
601        try {
602            if (mGetter == null) {
603                Class targetClass = target.getClass();
604                setupGetter(targetClass);
605                if (mGetter == null) {
606                    // Already logged the error - just return to avoid NPE
607                    return;
608                }
609            }
610            Object value = convertBack(mGetter.invoke(target));
611            kf.setValue(value);
612        } catch (InvocationTargetException e) {
613            Log.e("PropertyValuesHolder", e.toString());
614        } catch (IllegalAccessException e) {
615            Log.e("PropertyValuesHolder", e.toString());
616        }
617    }
618
619    /**
620     * This function is called by ObjectAnimator when setting the start values for an animation.
621     * The start values are set according to the current values in the target object. The
622     * property whose value is extracted is whatever is specified by the propertyName of this
623     * PropertyValuesHolder object.
624     *
625     * @param target The object which holds the start values that should be set.
626     */
627    void setupStartValue(Object target) {
628        setupValue(target, mKeyframeSet.mKeyframes.get(0));
629    }
630
631    /**
632     * This function is called by ObjectAnimator when setting the end values for an animation.
633     * The end values are set according to the current values in the target object. The
634     * property whose value is extracted is whatever is specified by the propertyName of this
635     * PropertyValuesHolder object.
636     *
637     * @param target The object which holds the start values that should be set.
638     */
639    void setupEndValue(Object target) {
640        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
641    }
642
643    @Override
644    public PropertyValuesHolder clone() {
645        try {
646            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
647            newPVH.mPropertyName = mPropertyName;
648            newPVH.mProperty = mProperty;
649            newPVH.mKeyframeSet = mKeyframeSet.clone();
650            newPVH.mEvaluator = mEvaluator;
651            return newPVH;
652        } catch (CloneNotSupportedException e) {
653            // won't reach here
654            return null;
655        }
656    }
657
658    /**
659     * Internal function to set the value on the target object, using the setter set up
660     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
661     * to handle turning the value calculated by ValueAnimator into a value set on the object
662     * according to the name of the property.
663     * @param target The target object on which the value is set
664     */
665    void setAnimatedValue(Object target) {
666        if (mProperty != null) {
667            mProperty.set(target, getAnimatedValue());
668        }
669        if (mSetter != null) {
670            try {
671                mTmpValueArray[0] = getAnimatedValue();
672                mSetter.invoke(target, mTmpValueArray);
673            } catch (InvocationTargetException e) {
674                Log.e("PropertyValuesHolder", e.toString());
675            } catch (IllegalAccessException e) {
676                Log.e("PropertyValuesHolder", e.toString());
677            }
678        }
679    }
680
681    /**
682     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
683     * to calculate animated values.
684     */
685    void init() {
686        if (mEvaluator == null) {
687            // We already handle int and float automatically, but not their Object
688            // equivalents
689            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
690                    (mValueType == Float.class) ? sFloatEvaluator :
691                    null;
692        }
693        if (mEvaluator != null) {
694            // KeyframeSet knows how to evaluate the common types - only give it a custom
695            // evaluator if one has been set on this class
696            mKeyframeSet.setEvaluator(mEvaluator);
697        }
698    }
699
700    /**
701     * The TypeEvaluator will be automatically determined based on the type of values
702     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
703     * desired. This may be important in cases where either the type of the values supplied
704     * do not match the way that they should be interpolated between, or if the values
705     * are of a custom type or one not currently understood by the animation system. Currently,
706     * only values of type float and int (and their Object equivalents: Float
707     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
708     * @param evaluator
709     */
710    public void setEvaluator(TypeEvaluator evaluator) {
711        mEvaluator = evaluator;
712        mKeyframeSet.setEvaluator(evaluator);
713    }
714
715    /**
716     * Function used to calculate the value according to the evaluator set up for
717     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
718     *
719     * @param fraction The elapsed, interpolated fraction of the animation.
720     */
721    void calculateValue(float fraction) {
722        Object value = mKeyframeSet.getValue(fraction);
723        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
724    }
725
726    /**
727     * Sets the name of the property that will be animated. This name is used to derive
728     * a setter function that will be called to set animated values.
729     * For example, a property name of <code>foo</code> will result
730     * in a call to the function <code>setFoo()</code> on the target object. If either
731     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
732     * also be derived and called.
733     *
734     * <p>Note that the setter function derived from this property name
735     * must take the same parameter type as the
736     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
737     * the setter function will fail.</p>
738     *
739     * @param propertyName The name of the property being animated.
740     */
741    public void setPropertyName(String propertyName) {
742        mPropertyName = propertyName;
743    }
744
745    /**
746     * Sets the property that will be animated.
747     *
748     * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
749     * must exist on the target object specified in that ObjectAnimator.</p>
750     *
751     * @param property The property being animated.
752     */
753    public void setProperty(Property property) {
754        mProperty = property;
755    }
756
757    /**
758     * Gets the name of the property that will be animated. This name will be used to derive
759     * a setter function that will be called to set animated values.
760     * For example, a property name of <code>foo</code> will result
761     * in a call to the function <code>setFoo()</code> on the target object. If either
762     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
763     * also be derived and called.
764     */
765    public String getPropertyName() {
766        return mPropertyName;
767    }
768
769    /**
770     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
771     * most recently calculated in calculateValue().
772     * @return
773     */
774    Object getAnimatedValue() {
775        return mAnimatedValue;
776    }
777
778    @Override
779    public String toString() {
780        return mPropertyName + ": " + mKeyframeSet.toString();
781    }
782
783    /**
784     * Utility method to derive a setter/getter method name from a property name, where the
785     * prefix is typically "set" or "get" and the first letter of the property name is
786     * capitalized.
787     *
788     * @param prefix The precursor to the method name, before the property name begins, typically
789     * "set" or "get".
790     * @param propertyName The name of the property that represents the bulk of the method name
791     * after the prefix. The first letter of this word will be capitalized in the resulting
792     * method name.
793     * @return String the property name converted to a method name according to the conventions
794     * specified above.
795     */
796    static String getMethodName(String prefix, String propertyName) {
797        if (propertyName == null || propertyName.length() == 0) {
798            // shouldn't get here
799            return prefix;
800        }
801        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
802        String theRest = propertyName.substring(1);
803        return prefix + firstLetter + theRest;
804    }
805
806    static class IntPropertyValuesHolder extends PropertyValuesHolder {
807
808        // Cache JNI functions to avoid looking them up twice
809        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
810                new HashMap<Class, HashMap<String, Integer>>();
811        int mJniSetter;
812        private IntProperty mIntProperty;
813
814        IntKeyframeSet mIntKeyframeSet;
815        int mIntAnimatedValue;
816
817        public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
818            super(propertyName);
819            mValueType = int.class;
820            mKeyframeSet = keyframeSet;
821            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
822        }
823
824        public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
825            super(property);
826            mValueType = int.class;
827            mKeyframeSet = keyframeSet;
828            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
829            if (property instanceof  IntProperty) {
830                mIntProperty = (IntProperty) mProperty;
831            }
832        }
833
834        public IntPropertyValuesHolder(String propertyName, int... values) {
835            super(propertyName);
836            setIntValues(values);
837        }
838
839        public IntPropertyValuesHolder(Property property, int... values) {
840            super(property);
841            setIntValues(values);
842            if (property instanceof  IntProperty) {
843                mIntProperty = (IntProperty) mProperty;
844            }
845        }
846
847        @Override
848        public void setIntValues(int... values) {
849            super.setIntValues(values);
850            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
851        }
852
853        @Override
854        void calculateValue(float fraction) {
855            mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
856        }
857
858        @Override
859        Object getAnimatedValue() {
860            return mIntAnimatedValue;
861        }
862
863        @Override
864        public IntPropertyValuesHolder clone() {
865            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
866            newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
867            return newPVH;
868        }
869
870        /**
871         * Internal function to set the value on the target object, using the setter set up
872         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
873         * to handle turning the value calculated by ValueAnimator into a value set on the object
874         * according to the name of the property.
875         * @param target The target object on which the value is set
876         */
877        @Override
878        void setAnimatedValue(Object target) {
879            if (mIntProperty != null) {
880                mIntProperty.setValue(target, mIntAnimatedValue);
881                return;
882            }
883            if (mProperty != null) {
884                mProperty.set(target, mIntAnimatedValue);
885                return;
886            }
887            if (mJniSetter != 0) {
888                nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
889                return;
890            }
891            if (mSetter != null) {
892                try {
893                    mTmpValueArray[0] = mIntAnimatedValue;
894                    mSetter.invoke(target, mTmpValueArray);
895                } catch (InvocationTargetException e) {
896                    Log.e("PropertyValuesHolder", e.toString());
897                } catch (IllegalAccessException e) {
898                    Log.e("PropertyValuesHolder", e.toString());
899                }
900            }
901        }
902
903        @Override
904        void setupSetter(Class targetClass) {
905            if (mProperty != null) {
906                return;
907            }
908            // Check new static hashmap<propName, int> for setter method
909            try {
910                mPropertyMapLock.writeLock().lock();
911                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
912                if (propertyMap != null) {
913                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
914                    if (mJniSetterInteger != null) {
915                        mJniSetter = mJniSetterInteger;
916                    }
917                }
918                if (mJniSetter == 0) {
919                    String methodName = getMethodName("set", mPropertyName);
920                    mJniSetter = nGetIntMethod(targetClass, methodName);
921                    if (mJniSetter != 0) {
922                        if (propertyMap == null) {
923                            propertyMap = new HashMap<String, Integer>();
924                            sJNISetterPropertyMap.put(targetClass, propertyMap);
925                        }
926                        propertyMap.put(mPropertyName, mJniSetter);
927                    }
928                }
929            } catch (NoSuchMethodError e) {
930                // Couldn't find it via JNI - try reflection next. Probably means the method
931                // doesn't exist, or the type is wrong. An error will be logged later if
932                // reflection fails as well.
933            } finally {
934                mPropertyMapLock.writeLock().unlock();
935            }
936            if (mJniSetter == 0) {
937                // Couldn't find method through fast JNI approach - just use reflection
938                super.setupSetter(targetClass);
939            }
940        }
941    }
942
943    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
944
945        // Cache JNI functions to avoid looking them up twice
946        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
947                new HashMap<Class, HashMap<String, Integer>>();
948        int mJniSetter;
949        private FloatProperty mFloatProperty;
950
951        FloatKeyframeSet mFloatKeyframeSet;
952        float mFloatAnimatedValue;
953
954        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
955            super(propertyName);
956            mValueType = float.class;
957            mKeyframeSet = keyframeSet;
958            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
959        }
960
961        public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
962            super(property);
963            mValueType = float.class;
964            mKeyframeSet = keyframeSet;
965            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
966            if (property instanceof FloatProperty) {
967                mFloatProperty = (FloatProperty) mProperty;
968            }
969        }
970
971        public FloatPropertyValuesHolder(String propertyName, float... values) {
972            super(propertyName);
973            setFloatValues(values);
974        }
975
976        public FloatPropertyValuesHolder(Property property, float... values) {
977            super(property);
978            setFloatValues(values);
979            if (property instanceof  FloatProperty) {
980                mFloatProperty = (FloatProperty) mProperty;
981            }
982        }
983
984        @Override
985        public void setFloatValues(float... values) {
986            super.setFloatValues(values);
987            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
988        }
989
990        @Override
991        void calculateValue(float fraction) {
992            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
993        }
994
995        @Override
996        Object getAnimatedValue() {
997            return mFloatAnimatedValue;
998        }
999
1000        @Override
1001        public FloatPropertyValuesHolder clone() {
1002            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
1003            newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
1004            return newPVH;
1005        }
1006
1007        /**
1008         * Internal function to set the value on the target object, using the setter set up
1009         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
1010         * to handle turning the value calculated by ValueAnimator into a value set on the object
1011         * according to the name of the property.
1012         * @param target The target object on which the value is set
1013         */
1014        @Override
1015        void setAnimatedValue(Object target) {
1016            if (mFloatProperty != null) {
1017                mFloatProperty.setValue(target, mFloatAnimatedValue);
1018                return;
1019            }
1020            if (mProperty != null) {
1021                mProperty.set(target, mFloatAnimatedValue);
1022                return;
1023            }
1024            if (mJniSetter != 0) {
1025                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
1026                return;
1027            }
1028            if (mSetter != null) {
1029                try {
1030                    mTmpValueArray[0] = mFloatAnimatedValue;
1031                    mSetter.invoke(target, mTmpValueArray);
1032                } catch (InvocationTargetException e) {
1033                    Log.e("PropertyValuesHolder", e.toString());
1034                } catch (IllegalAccessException e) {
1035                    Log.e("PropertyValuesHolder", e.toString());
1036                }
1037            }
1038        }
1039
1040        @Override
1041        void setupSetter(Class targetClass) {
1042            if (mProperty != null) {
1043                return;
1044            }
1045            // Check new static hashmap<propName, int> for setter method
1046            try {
1047                mPropertyMapLock.writeLock().lock();
1048                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
1049                if (propertyMap != null) {
1050                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
1051                    if (mJniSetterInteger != null) {
1052                        mJniSetter = mJniSetterInteger;
1053                    }
1054                }
1055                if (mJniSetter == 0) {
1056                    String methodName = getMethodName("set", mPropertyName);
1057                    mJniSetter = nGetFloatMethod(targetClass, methodName);
1058                    if (mJniSetter != 0) {
1059                        if (propertyMap == null) {
1060                            propertyMap = new HashMap<String, Integer>();
1061                            sJNISetterPropertyMap.put(targetClass, propertyMap);
1062                        }
1063                        propertyMap.put(mPropertyName, mJniSetter);
1064                    }
1065                }
1066            } catch (NoSuchMethodError e) {
1067                // Couldn't find it via JNI - try reflection next. Probably means the method
1068                // doesn't exist, or the type is wrong. An error will be logged later if
1069                // reflection fails as well.
1070            } finally {
1071                mPropertyMapLock.writeLock().unlock();
1072            }
1073            if (mJniSetter == 0) {
1074                // Couldn't find method through fast JNI approach - just use reflection
1075                super.setupSetter(targetClass);
1076            }
1077        }
1078
1079    }
1080
1081    native static private int nGetIntMethod(Class targetClass, String methodName);
1082    native static private int nGetFloatMethod(Class targetClass, String methodName);
1083    native static private void nCallIntMethod(Object target, int methodID, int arg);
1084    native static private void nCallFloatMethod(Object target, int methodID, float arg);
1085}