create changelog entry
[debian/openrocket] / android-libraries / ActionBarSherlock / src / com / actionbarsherlock / internal / nineoldandroids / animation / PropertyValuesHolder.java
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
17 package com.actionbarsherlock.internal.nineoldandroids.animation;
18
19 //import android.util.FloatProperty;
20 //import android.util.IntProperty;
21 import android.util.Log;
22 //import android.util.Property;
23
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.HashMap;
27 import 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  */
35 @SuppressWarnings({"rawtypes", "unchecked"})
36 public class PropertyValuesHolder implements Cloneable {
37
38     /**
39      * The name of the property associated with the values. This need not be a real property,
40      * unless this object is being used with ObjectAnimator. But this is the name by which
41      * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
42      */
43     String mPropertyName;
44
45     /**
46      * @hide
47      */
48     //protected Property mProperty;
49
50     /**
51      * The setter function, if needed. ObjectAnimator hands off this functionality to
52      * PropertyValuesHolder, since it holds all of the per-property information. This
53      * property is automatically
54      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
55      */
56     Method mSetter = null;
57
58     /**
59      * The getter function, if needed. ObjectAnimator hands off this functionality to
60      * PropertyValuesHolder, since it holds all of the per-property information. This
61      * property is automatically
62      * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
63      * The getter is only derived and used if one of the values is null.
64      */
65     private Method mGetter = null;
66
67     /**
68      * The type of values supplied. This information is used both in deriving the setter/getter
69      * functions and in deriving the type of TypeEvaluator.
70      */
71     Class mValueType;
72
73     /**
74      * The set of keyframes (time/value pairs) that define this animation.
75      */
76     KeyframeSet mKeyframeSet = null;
77
78
79     // type evaluators for the primitive types handled by this implementation
80     private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
81     private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
82
83     // We try several different types when searching for appropriate setter/getter functions.
84     // The caller may have supplied values in a type that does not match the setter/getter
85     // functions (such as the integers 0 and 1 to represent floating point values for alpha).
86     // Also, the use of generics in constructors means that we end up with the Object versions
87     // of primitive types (Float vs. float). But most likely, the setter/getter functions
88     // will take primitive types instead.
89     // So we supply an ordered array of other types to try before giving up.
90     private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
91             Double.class, Integer.class};
92     private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
93             Float.class, Double.class};
94     private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
95             Float.class, Integer.class};
96
97     // These maps hold all property entries for a particular class. This map
98     // is used to speed up property/setter/getter lookups for a given class/property
99     // combination. No need to use reflection on the combination more than once.
100     private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
101             new HashMap<Class, HashMap<String, Method>>();
102     private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
103             new HashMap<Class, HashMap<String, Method>>();
104
105     // This lock is used to ensure that only one thread is accessing the property maps
106     // at a time.
107     final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
108
109     // Used to pass single value to varargs parameter in setter invocation
110     final Object[] mTmpValueArray = new Object[1];
111
112     /**
113      * The type evaluator used to calculate the animated values. This evaluator is determined
114      * automatically based on the type of the start/end objects passed into the constructor,
115      * but the system only knows about the primitive types int and float. Any other
116      * type will need to set the evaluator to a custom evaluator for that type.
117      */
118     private TypeEvaluator mEvaluator;
119
120     /**
121      * The value most recently calculated by calculateValue(). This is set during
122      * that function and might be retrieved later either by ValueAnimator.animatedValue() or
123      * by the property-setting logic in ObjectAnimator.animatedValue().
124      */
125     private Object mAnimatedValue;
126
127     /**
128      * Internal utility constructor, used by the factory methods to set the property name.
129      * @param propertyName The name of the property for this holder.
130      */
131     private PropertyValuesHolder(String propertyName) {
132         mPropertyName = propertyName;
133     }
134
135     /**
136      * Internal utility constructor, used by the factory methods to set the property.
137      * @param property The property for this holder.
138      */
139     //private PropertyValuesHolder(Property property) {
140     //    mProperty = property;
141     //    if (property != null) {
142     //        mPropertyName = property.getName();
143     //    }
144     //}
145
146     /**
147      * Constructs and returns a PropertyValuesHolder with a given property name and
148      * set of int values.
149      * @param propertyName The name of the property being animated.
150      * @param values The values that the named property will animate between.
151      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
152      */
153     public static PropertyValuesHolder ofInt(String propertyName, int... values) {
154         return new IntPropertyValuesHolder(propertyName, values);
155     }
156
157     /**
158      * Constructs and returns a PropertyValuesHolder with a given property and
159      * set of int values.
160      * @param property The property being animated. Should not be null.
161      * @param values The values that the property will animate between.
162      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
163      */
164     //public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
165     //    return new IntPropertyValuesHolder(property, values);
166     //}
167
168     /**
169      * Constructs and returns a PropertyValuesHolder with a given property name and
170      * set of float values.
171      * @param propertyName The name of the property being animated.
172      * @param values The values that the named property will animate between.
173      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
174      */
175     public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
176         return new FloatPropertyValuesHolder(propertyName, values);
177     }
178
179     /**
180      * Constructs and returns a PropertyValuesHolder with a given property and
181      * set of float values.
182      * @param property The property being animated. Should not be null.
183      * @param values The values that the property will animate between.
184      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
185      */
186     //public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
187     //    return new FloatPropertyValuesHolder(property, values);
188     //}
189
190     /**
191      * Constructs and returns a PropertyValuesHolder with a given property name and
192      * set of Object values. This variant also takes a TypeEvaluator because the system
193      * cannot automatically interpolate between objects of unknown type.
194      *
195      * @param propertyName The name of the property being animated.
196      * @param evaluator A TypeEvaluator that will be called on each animation frame to
197      * provide the necessary interpolation between the Object values to derive the animated
198      * value.
199      * @param values The values that the named property will animate between.
200      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
201      */
202     public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
203             Object... values) {
204         PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
205         pvh.setObjectValues(values);
206         pvh.setEvaluator(evaluator);
207         return pvh;
208     }
209
210     /**
211      * Constructs and returns a PropertyValuesHolder with a given property and
212      * set of Object values. This variant also takes a TypeEvaluator because the system
213      * cannot automatically interpolate between objects of unknown type.
214      *
215      * @param property The property being animated. Should not be null.
216      * @param evaluator A TypeEvaluator that will be called on each animation frame to
217      * provide the necessary interpolation between the Object values to derive the animated
218      * value.
219      * @param values The values that the property will animate between.
220      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
221      */
222     //public static <V> PropertyValuesHolder ofObject(Property property,
223     //        TypeEvaluator<V> evaluator, V... values) {
224     //    PropertyValuesHolder pvh = new PropertyValuesHolder(property);
225     //    pvh.setObjectValues(values);
226     //    pvh.setEvaluator(evaluator);
227     //    return pvh;
228     //}
229
230     /**
231      * Constructs and returns a PropertyValuesHolder object with the specified property name and set
232      * of values. These values can be of any type, but the type should be consistent so that
233      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
234      * the common type.
235      * <p>If there is only one value, it is assumed to be the end value of an animation,
236      * and an initial value will be derived, if possible, by calling a getter function
237      * on the object. Also, if any value is null, the value will be filled in when the animation
238      * starts in the same way. This mechanism of automatically getting null values only works
239      * if the PropertyValuesHolder object is used in conjunction
240      * {@link ObjectAnimator}, and with a getter function
241      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
242      * no way of determining what the value should be.
243      * @param propertyName The name of the property associated with this set of values. This
244      * can be the actual property name to be used when using a ObjectAnimator object, or
245      * just a name used to get animated values, such as if this object is used with an
246      * ValueAnimator object.
247      * @param values The set of values to animate between.
248      */
249     public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
250         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
251         if (keyframeSet instanceof IntKeyframeSet) {
252             return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
253         } else if (keyframeSet instanceof FloatKeyframeSet) {
254             return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
255         }
256         else {
257             PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
258             pvh.mKeyframeSet = keyframeSet;
259             pvh.mValueType = values[0].getType();
260             return pvh;
261         }
262     }
263
264     /**
265      * Constructs and returns a PropertyValuesHolder object with the specified property and set
266      * of values. These values can be of any type, but the type should be consistent so that
267      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
268      * the common type.
269      * <p>If there is only one value, it is assumed to be the end value of an animation,
270      * and an initial value will be derived, if possible, by calling the property's
271      * {@link android.util.Property#get(Object)} function.
272      * Also, if any value is null, the value will be filled in when the animation
273      * starts in the same way. This mechanism of automatically getting null values only works
274      * if the PropertyValuesHolder object is used in conjunction with
275      * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
276      * no way of determining what the value should be.
277      * @param property The property associated with this set of values. Should not be null.
278      * @param values The set of values to animate between.
279      */
280     //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
281     //    KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
282     //    if (keyframeSet instanceof IntKeyframeSet) {
283     //        return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
284     //    } else if (keyframeSet instanceof FloatKeyframeSet) {
285     //        return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
286     //    }
287     //    else {
288     //        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
289     //        pvh.mKeyframeSet = keyframeSet;
290     //        pvh.mValueType = ((Keyframe)values[0]).getType();
291     //        return pvh;
292     //    }
293     //}
294
295     /**
296      * Set the animated values for this object to this set of ints.
297      * If there is only one value, it is assumed to be the end value of an animation,
298      * and an initial value will be derived, if possible, by calling a getter function
299      * on the object. Also, if any value is null, the value will be filled in when the animation
300      * starts in the same way. This mechanism of automatically getting null values only works
301      * if the PropertyValuesHolder object is used in conjunction
302      * {@link ObjectAnimator}, and with a getter function
303      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
304      * no way of determining what the value should be.
305      *
306      * @param values One or more values that the animation will animate between.
307      */
308     public void setIntValues(int... values) {
309         mValueType = int.class;
310         mKeyframeSet = KeyframeSet.ofInt(values);
311     }
312
313     /**
314      * Set the animated values for this object to this set of floats.
315      * If there is only one value, it is assumed to be the end value of an animation,
316      * and an initial value will be derived, if possible, by calling a getter function
317      * on the object. Also, if any value is null, the value will be filled in when the animation
318      * starts in the same way. This mechanism of automatically getting null values only works
319      * if the PropertyValuesHolder object is used in conjunction
320      * {@link ObjectAnimator}, and with a getter function
321      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
322      * no way of determining what the value should be.
323      *
324      * @param values One or more values that the animation will animate between.
325      */
326     public void setFloatValues(float... values) {
327         mValueType = float.class;
328         mKeyframeSet = KeyframeSet.ofFloat(values);
329     }
330
331     /**
332      * Set the animated values for this object to this set of Keyframes.
333      *
334      * @param values One or more values that the animation will animate between.
335      */
336     public void setKeyframes(Keyframe... values) {
337         int numKeyframes = values.length;
338         Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
339         mValueType = values[0].getType();
340         for (int i = 0; i < numKeyframes; ++i) {
341             keyframes[i] = values[i];
342         }
343         mKeyframeSet = new KeyframeSet(keyframes);
344     }
345
346     /**
347      * Set the animated values for this object to this set of Objects.
348      * If there is only one value, it is assumed to be the end value of an animation,
349      * and an initial value will be derived, if possible, by calling a getter function
350      * on the object. Also, if any value is null, the value will be filled in when the animation
351      * starts in the same way. This mechanism of automatically getting null values only works
352      * if the PropertyValuesHolder object is used in conjunction
353      * {@link ObjectAnimator}, and with a getter function
354      * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
355      * no way of determining what the value should be.
356      *
357      * @param values One or more values that the animation will animate between.
358      */
359     public void setObjectValues(Object... values) {
360         mValueType = values[0].getClass();
361         mKeyframeSet = KeyframeSet.ofObject(values);
362     }
363
364     /**
365      * Determine the setter or getter function using the JavaBeans convention of setFoo or
366      * getFoo for a property named 'foo'. This function figures out what the name of the
367      * function should be and uses reflection to find the Method with that name on the
368      * target object.
369      *
370      * @param targetClass The class to search for the method
371      * @param prefix "set" or "get", depending on whether we need a setter or getter.
372      * @param valueType The type of the parameter (in the case of a setter). This type
373      * is derived from the values set on this PropertyValuesHolder. This type is used as
374      * a first guess at the parameter type, but we check for methods with several different
375      * types to avoid problems with slight mis-matches between supplied values and actual
376      * value types used on the setter.
377      * @return Method the method associated with mPropertyName.
378      */
379     private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
380         // TODO: faster implementation...
381         Method returnVal = null;
382         String methodName = getMethodName(prefix, mPropertyName);
383         Class args[] = null;
384         if (valueType == null) {
385             try {
386                 returnVal = targetClass.getMethod(methodName, args);
387             } catch (NoSuchMethodException e) {
388                 Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
389                         "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
390             }
391         } else {
392             args = new Class[1];
393             Class typeVariants[];
394             if (mValueType.equals(Float.class)) {
395                 typeVariants = FLOAT_VARIANTS;
396             } else if (mValueType.equals(Integer.class)) {
397                 typeVariants = INTEGER_VARIANTS;
398             } else if (mValueType.equals(Double.class)) {
399                 typeVariants = DOUBLE_VARIANTS;
400             } else {
401                 typeVariants = new Class[1];
402                 typeVariants[0] = mValueType;
403             }
404             for (Class typeVariant : typeVariants) {
405                 args[0] = typeVariant;
406                 try {
407                     returnVal = targetClass.getMethod(methodName, args);
408                     // change the value type to suit
409                     mValueType = typeVariant;
410                     return returnVal;
411                 } catch (NoSuchMethodException e) {
412                     // Swallow the error and keep trying other variants
413                 }
414             }
415             // If we got here, then no appropriate function was found
416             Log.e("PropertyValuesHolder",
417                     "Couldn't find " + prefix + "ter property " + mPropertyName +
418                             " for " + targetClass.getSimpleName() +
419                             " with value type "+ mValueType);
420         }
421
422         return returnVal;
423     }
424
425
426     /**
427      * Returns the setter or getter requested. This utility function checks whether the
428      * requested method exists in the propertyMapMap cache. If not, it calls another
429      * utility function to request the Method from the targetClass directly.
430      * @param targetClass The Class on which the requested method should exist.
431      * @param propertyMapMap The cache of setters/getters derived so far.
432      * @param prefix "set" or "get", for the setter or getter.
433      * @param valueType The type of parameter passed into the method (null for getter).
434      * @return Method the method associated with mPropertyName.
435      */
436     private Method setupSetterOrGetter(Class targetClass,
437             HashMap<Class, HashMap<String, Method>> propertyMapMap,
438             String prefix, Class valueType) {
439         Method setterOrGetter = null;
440         try {
441             // Have to lock property map prior to reading it, to guard against
442             // another thread putting something in there after we've checked it
443             // but before we've added an entry to it
444             mPropertyMapLock.writeLock().lock();
445             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
446             if (propertyMap != null) {
447                 setterOrGetter = propertyMap.get(mPropertyName);
448             }
449             if (setterOrGetter == null) {
450                 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
451                 if (propertyMap == null) {
452                     propertyMap = new HashMap<String, Method>();
453                     propertyMapMap.put(targetClass, propertyMap);
454                 }
455                 propertyMap.put(mPropertyName, setterOrGetter);
456             }
457         } finally {
458             mPropertyMapLock.writeLock().unlock();
459         }
460         return setterOrGetter;
461     }
462
463     /**
464      * Utility function to get the setter from targetClass
465      * @param targetClass The Class on which the requested method should exist.
466      */
467     void setupSetter(Class targetClass) {
468         mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
469     }
470
471     /**
472      * Utility function to get the getter from targetClass
473      */
474     private void setupGetter(Class targetClass) {
475         mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
476     }
477
478     /**
479      * Internal function (called from ObjectAnimator) to set up the setter and getter
480      * prior to running the animation. If the setter has not been manually set for this
481      * object, it will be derived automatically given the property name, target object, and
482      * types of values supplied. If no getter has been set, it will be supplied iff any of the
483      * supplied values was null. If there is a null value, then the getter (supplied or derived)
484      * will be called to set those null values to the current value of the property
485      * on the target object.
486      * @param target The object on which the setter (and possibly getter) exist.
487      */
488     void setupSetterAndGetter(Object target) {
489         //if (mProperty != null) {
490         //    // check to make sure that mProperty is on the class of target
491         //    try {
492         //        Object testValue = mProperty.get(target);
493         //        for (Keyframe kf : mKeyframeSet.mKeyframes) {
494         //            if (!kf.hasValue()) {
495         //                kf.setValue(mProperty.get(target));
496         //            }
497         //        }
498         //        return;
499         //    } catch (ClassCastException e) {
500         //        Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
501         //                ") on target object " + target + ". Trying reflection instead");
502         //        mProperty = null;
503         //    }
504         //}
505         Class targetClass = target.getClass();
506         if (mSetter == null) {
507             setupSetter(targetClass);
508         }
509         for (Keyframe kf : mKeyframeSet.mKeyframes) {
510             if (!kf.hasValue()) {
511                 if (mGetter == null) {
512                     setupGetter(targetClass);
513                 }
514                 try {
515                     kf.setValue(mGetter.invoke(target));
516                 } catch (InvocationTargetException e) {
517                     Log.e("PropertyValuesHolder", e.toString());
518                 } catch (IllegalAccessException e) {
519                     Log.e("PropertyValuesHolder", e.toString());
520                 }
521             }
522         }
523     }
524
525     /**
526      * Utility function to set the value stored in a particular Keyframe. The value used is
527      * whatever the value is for the property name specified in the keyframe on the target object.
528      *
529      * @param target The target object from which the current value should be extracted.
530      * @param kf The keyframe which holds the property name and value.
531      */
532     private void setupValue(Object target, Keyframe kf) {
533         //if (mProperty != null) {
534         //    kf.setValue(mProperty.get(target));
535         //}
536         try {
537             if (mGetter == null) {
538                 Class targetClass = target.getClass();
539                 setupGetter(targetClass);
540             }
541             kf.setValue(mGetter.invoke(target));
542         } catch (InvocationTargetException e) {
543             Log.e("PropertyValuesHolder", e.toString());
544         } catch (IllegalAccessException e) {
545             Log.e("PropertyValuesHolder", e.toString());
546         }
547     }
548
549     /**
550      * This function is called by ObjectAnimator when setting the start values for an animation.
551      * The start values are set according to the current values in the target object. The
552      * property whose value is extracted is whatever is specified by the propertyName of this
553      * PropertyValuesHolder object.
554      *
555      * @param target The object which holds the start values that should be set.
556      */
557     void setupStartValue(Object target) {
558         setupValue(target, mKeyframeSet.mKeyframes.get(0));
559     }
560
561     /**
562      * This function is called by ObjectAnimator when setting the end values for an animation.
563      * The end values are set according to the current values in the target object. The
564      * property whose value is extracted is whatever is specified by the propertyName of this
565      * PropertyValuesHolder object.
566      *
567      * @param target The object which holds the start values that should be set.
568      */
569     void setupEndValue(Object target) {
570         setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
571     }
572
573     @Override
574     public PropertyValuesHolder clone() {
575         try {
576             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
577             newPVH.mPropertyName = mPropertyName;
578             //newPVH.mProperty = mProperty;
579             newPVH.mKeyframeSet = mKeyframeSet.clone();
580             newPVH.mEvaluator = mEvaluator;
581             return newPVH;
582         } catch (CloneNotSupportedException e) {
583             // won't reach here
584             return null;
585         }
586     }
587
588     /**
589      * Internal function to set the value on the target object, using the setter set up
590      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
591      * to handle turning the value calculated by ValueAnimator into a value set on the object
592      * according to the name of the property.
593      * @param target The target object on which the value is set
594      */
595     void setAnimatedValue(Object target) {
596         //if (mProperty != null) {
597         //    mProperty.set(target, getAnimatedValue());
598         //}
599         if (mSetter != null) {
600             try {
601                 mTmpValueArray[0] = getAnimatedValue();
602                 mSetter.invoke(target, mTmpValueArray);
603             } catch (InvocationTargetException e) {
604                 Log.e("PropertyValuesHolder", e.toString());
605             } catch (IllegalAccessException e) {
606                 Log.e("PropertyValuesHolder", e.toString());
607             }
608         }
609     }
610
611     /**
612      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
613      * to calculate animated values.
614      */
615     void init() {
616         if (mEvaluator == null) {
617             // We already handle int and float automatically, but not their Object
618             // equivalents
619             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
620                     (mValueType == Float.class) ? sFloatEvaluator :
621                     null;
622         }
623         if (mEvaluator != null) {
624             // KeyframeSet knows how to evaluate the common types - only give it a custom
625             // evaluator if one has been set on this class
626             mKeyframeSet.setEvaluator(mEvaluator);
627         }
628     }
629
630     /**
631      * The TypeEvaluator will the automatically determined based on the type of values
632      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
633      * desired. This may be important in cases where either the type of the values supplied
634      * do not match the way that they should be interpolated between, or if the values
635      * are of a custom type or one not currently understood by the animation system. Currently,
636      * only values of type float and int (and their Object equivalents: Float
637      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
638      * @param evaluator
639      */
640     public void setEvaluator(TypeEvaluator evaluator) {
641         mEvaluator = evaluator;
642         mKeyframeSet.setEvaluator(evaluator);
643     }
644
645     /**
646      * Function used to calculate the value according to the evaluator set up for
647      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
648      *
649      * @param fraction The elapsed, interpolated fraction of the animation.
650      */
651     void calculateValue(float fraction) {
652         mAnimatedValue = mKeyframeSet.getValue(fraction);
653     }
654
655     /**
656      * Sets the name of the property that will be animated. This name is used to derive
657      * a setter function that will be called to set animated values.
658      * For example, a property name of <code>foo</code> will result
659      * in a call to the function <code>setFoo()</code> on the target object. If either
660      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
661      * also be derived and called.
662      *
663      * <p>Note that the setter function derived from this property name
664      * must take the same parameter type as the
665      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
666      * the setter function will fail.</p>
667      *
668      * @param propertyName The name of the property being animated.
669      */
670     public void setPropertyName(String propertyName) {
671         mPropertyName = propertyName;
672     }
673
674     /**
675      * Sets the property that will be animated.
676      *
677      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
678      * must exist on the target object specified in that ObjectAnimator.</p>
679      *
680      * @param property The property being animated.
681      */
682     //public void setProperty(Property property) {
683     //    mProperty = property;
684     //}
685
686     /**
687      * Gets the name of the property that will be animated. This name will be used to derive
688      * a setter function that will be called to set animated values.
689      * For example, a property name of <code>foo</code> will result
690      * in a call to the function <code>setFoo()</code> on the target object. If either
691      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
692      * also be derived and called.
693      */
694     public String getPropertyName() {
695         return mPropertyName;
696     }
697
698     /**
699      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
700      * most recently calculated in calculateValue().
701      * @return
702      */
703     Object getAnimatedValue() {
704         return mAnimatedValue;
705     }
706
707     @Override
708     public String toString() {
709         return mPropertyName + ": " + mKeyframeSet.toString();
710     }
711
712     /**
713      * Utility method to derive a setter/getter method name from a property name, where the
714      * prefix is typically "set" or "get" and the first letter of the property name is
715      * capitalized.
716      *
717      * @param prefix The precursor to the method name, before the property name begins, typically
718      * "set" or "get".
719      * @param propertyName The name of the property that represents the bulk of the method name
720      * after the prefix. The first letter of this word will be capitalized in the resulting
721      * method name.
722      * @return String the property name converted to a method name according to the conventions
723      * specified above.
724      */
725     static String getMethodName(String prefix, String propertyName) {
726         if (propertyName == null || propertyName.length() == 0) {
727             // shouldn't get here
728             return prefix;
729         }
730         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
731         String theRest = propertyName.substring(1);
732         return prefix + firstLetter + theRest;
733     }
734
735     static class IntPropertyValuesHolder extends PropertyValuesHolder {
736
737         // Cache JNI functions to avoid looking them up twice
738         //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
739         //        new HashMap<Class, HashMap<String, Integer>>();
740         //int mJniSetter;
741         //private IntProperty mIntProperty;
742
743         IntKeyframeSet mIntKeyframeSet;
744         int mIntAnimatedValue;
745
746         public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
747             super(propertyName);
748             mValueType = int.class;
749             mKeyframeSet = keyframeSet;
750             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
751         }
752
753         //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
754         //    super(property);
755         //    mValueType = int.class;
756         //    mKeyframeSet = keyframeSet;
757         //    mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
758         //    if (property instanceof  IntProperty) {
759         //        mIntProperty = (IntProperty) mProperty;
760         //    }
761         //}
762
763         public IntPropertyValuesHolder(String propertyName, int... values) {
764             super(propertyName);
765             setIntValues(values);
766         }
767
768         //public IntPropertyValuesHolder(Property property, int... values) {
769         //    super(property);
770         //    setIntValues(values);
771         //    if (property instanceof  IntProperty) {
772         //        mIntProperty = (IntProperty) mProperty;
773         //    }
774         //}
775
776         @Override
777         public void setIntValues(int... values) {
778             super.setIntValues(values);
779             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
780         }
781
782         @Override
783         void calculateValue(float fraction) {
784             mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
785         }
786
787         @Override
788         Object getAnimatedValue() {
789             return mIntAnimatedValue;
790         }
791
792         @Override
793         public IntPropertyValuesHolder clone() {
794             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
795             newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
796             return newPVH;
797         }
798
799         /**
800          * Internal function to set the value on the target object, using the setter set up
801          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
802          * to handle turning the value calculated by ValueAnimator into a value set on the object
803          * according to the name of the property.
804          * @param target The target object on which the value is set
805          */
806         @Override
807         void setAnimatedValue(Object target) {
808             //if (mIntProperty != null) {
809             //    mIntProperty.setValue(target, mIntAnimatedValue);
810             //    return;
811             //}
812             //if (mProperty != null) {
813             //    mProperty.set(target, mIntAnimatedValue);
814             //    return;
815             //}
816             //if (mJniSetter != 0) {
817             //    nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
818             //    return;
819             //}
820             if (mSetter != null) {
821                 try {
822                     mTmpValueArray[0] = mIntAnimatedValue;
823                     mSetter.invoke(target, mTmpValueArray);
824                 } catch (InvocationTargetException e) {
825                     Log.e("PropertyValuesHolder", e.toString());
826                 } catch (IllegalAccessException e) {
827                     Log.e("PropertyValuesHolder", e.toString());
828                 }
829             }
830         }
831
832         @Override
833         void setupSetter(Class targetClass) {
834             //if (mProperty != null) {
835             //    return;
836             //}
837             // Check new static hashmap<propName, int> for setter method
838             //try {
839             //    mPropertyMapLock.writeLock().lock();
840             //    HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
841             //    if (propertyMap != null) {
842             //        Integer mJniSetterInteger = propertyMap.get(mPropertyName);
843             //        if (mJniSetterInteger != null) {
844             //            mJniSetter = mJniSetterInteger;
845             //        }
846             //    }
847             //    if (mJniSetter == 0) {
848             //        String methodName = getMethodName("set", mPropertyName);
849             //        mJniSetter = nGetIntMethod(targetClass, methodName);
850             //        if (mJniSetter != 0) {
851             //            if (propertyMap == null) {
852             //                propertyMap = new HashMap<String, Integer>();
853             //                sJNISetterPropertyMap.put(targetClass, propertyMap);
854             //            }
855             //            propertyMap.put(mPropertyName, mJniSetter);
856             //        }
857             //    }
858             //} catch (NoSuchMethodError e) {
859             //    Log.d("PropertyValuesHolder",
860             //            "Can't find native method using JNI, use reflection" + e);
861             //} finally {
862             //    mPropertyMapLock.writeLock().unlock();
863             //}
864             //if (mJniSetter == 0) {
865                 // Couldn't find method through fast JNI approach - just use reflection
866                 super.setupSetter(targetClass);
867             //}
868         }
869     }
870
871     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
872
873         // Cache JNI functions to avoid looking them up twice
874         //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
875         //        new HashMap<Class, HashMap<String, Integer>>();
876         //int mJniSetter;
877         //private FloatProperty mFloatProperty;
878
879         FloatKeyframeSet mFloatKeyframeSet;
880         float mFloatAnimatedValue;
881
882         public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
883             super(propertyName);
884             mValueType = float.class;
885             mKeyframeSet = keyframeSet;
886             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
887         }
888
889         //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
890         //    super(property);
891         //    mValueType = float.class;
892         //    mKeyframeSet = keyframeSet;
893         //    mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
894         //    if (property instanceof FloatProperty) {
895         //        mFloatProperty = (FloatProperty) mProperty;
896         //    }
897         //}
898
899         public FloatPropertyValuesHolder(String propertyName, float... values) {
900             super(propertyName);
901             setFloatValues(values);
902         }
903
904         //public FloatPropertyValuesHolder(Property property, float... values) {
905         //    super(property);
906         //    setFloatValues(values);
907         //    if (property instanceof  FloatProperty) {
908         //        mFloatProperty = (FloatProperty) mProperty;
909         //    }
910         //}
911
912         @Override
913         public void setFloatValues(float... values) {
914             super.setFloatValues(values);
915             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
916         }
917
918         @Override
919         void calculateValue(float fraction) {
920             mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
921         }
922
923         @Override
924         Object getAnimatedValue() {
925             return mFloatAnimatedValue;
926         }
927
928         @Override
929         public FloatPropertyValuesHolder clone() {
930             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
931             newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
932             return newPVH;
933         }
934
935         /**
936          * Internal function to set the value on the target object, using the setter set up
937          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
938          * to handle turning the value calculated by ValueAnimator into a value set on the object
939          * according to the name of the property.
940          * @param target The target object on which the value is set
941          */
942         @Override
943         void setAnimatedValue(Object target) {
944             //if (mFloatProperty != null) {
945             //    mFloatProperty.setValue(target, mFloatAnimatedValue);
946             //    return;
947             //}
948             //if (mProperty != null) {
949             //    mProperty.set(target, mFloatAnimatedValue);
950             //    return;
951             //}
952             //if (mJniSetter != 0) {
953             //    nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
954             //    return;
955             //}
956             if (mSetter != null) {
957                 try {
958                     mTmpValueArray[0] = mFloatAnimatedValue;
959                     mSetter.invoke(target, mTmpValueArray);
960                 } catch (InvocationTargetException e) {
961                     Log.e("PropertyValuesHolder", e.toString());
962                 } catch (IllegalAccessException e) {
963                     Log.e("PropertyValuesHolder", e.toString());
964                 }
965             }
966         }
967
968         @Override
969         void setupSetter(Class targetClass) {
970             //if (mProperty != null) {
971             //    return;
972             //}
973             // Check new static hashmap<propName, int> for setter method
974             //try {
975             //    mPropertyMapLock.writeLock().lock();
976             //    HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
977             //    if (propertyMap != null) {
978             //        Integer mJniSetterInteger = propertyMap.get(mPropertyName);
979             //        if (mJniSetterInteger != null) {
980             //            mJniSetter = mJniSetterInteger;
981             //        }
982             //    }
983             //    if (mJniSetter == 0) {
984             //        String methodName = getMethodName("set", mPropertyName);
985             //        mJniSetter = nGetFloatMethod(targetClass, methodName);
986             //        if (mJniSetter != 0) {
987             //            if (propertyMap == null) {
988             //                propertyMap = new HashMap<String, Integer>();
989             //                sJNISetterPropertyMap.put(targetClass, propertyMap);
990             //            }
991             //            propertyMap.put(mPropertyName, mJniSetter);
992             //        }
993             //    }
994             //} catch (NoSuchMethodError e) {
995             //    Log.d("PropertyValuesHolder",
996             //            "Can't find native method using JNI, use reflection" + e);
997             //} finally {
998             //    mPropertyMapLock.writeLock().unlock();
999             //}
1000             //if (mJniSetter == 0) {
1001                 // Couldn't find method through fast JNI approach - just use reflection
1002                 super.setupSetter(targetClass);
1003             //}
1004         }
1005
1006     }
1007
1008     //native static private int nGetIntMethod(Class targetClass, String methodName);
1009     //native static private int nGetFloatMethod(Class targetClass, String methodName);
1010     //native static private void nCallIntMethod(Object target, int methodID, int arg);
1011     //native static private void nCallFloatMethod(Object target, int methodID, float arg);
1012 }