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