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         mKeyframes.invalidateCache();
    811         if (mProperty != null) {
    812             // check to make sure that mProperty is on the class of target
    813             try {
    814                 Object testValue = null;
    815                 List<Keyframe> keyframes = mKeyframes.getKeyframes();
    816                 int keyframeCount = keyframes == null ? 0 : keyframes.size();
    817                 for (int i = 0; i < keyframeCount; i++) {
    818                     Keyframe kf = keyframes.get(i);
    819                     if (!kf.hasValue() || kf.valueWasSetOnStart()) {
    820                         if (testValue == null) {
    821                             testValue = convertBack(mProperty.get(target));
    822                         }
    823                         kf.setValue(testValue);
    824                         kf.setValueWasSetOnStart(true);
    825                     }
    826                 }
    827                 return;
    828             } catch (ClassCastException e) {
    829                 Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
    830                         ") on target object " + target + ". Trying reflection instead");
    831                 mProperty = null;
    832             }
    833         }
    834         // We can't just say 'else' here because the catch statement sets mProperty to null.
    835         if (mProperty == null) {
    836             Class targetClass = target.getClass();
    837             if (mSetter == null) {
    838                 setupSetter(targetClass);
    839             }
    840             List<Keyframe> keyframes = mKeyframes.getKeyframes();
    841             int keyframeCount = keyframes == null ? 0 : keyframes.size();
    842             for (int i = 0; i < keyframeCount; i++) {
    843                 Keyframe kf = keyframes.get(i);
    844                 if (!kf.hasValue() || kf.valueWasSetOnStart()) {
    845                     if (mGetter == null) {
    846                         setupGetter(targetClass);
    847                         if (mGetter == null) {
    848                             // Already logged the error - just return to avoid NPE
    849                             return;
    850                         }
    851                     }
    852                     try {
    853                         Object value = convertBack(mGetter.invoke(target));
    854                         kf.setValue(value);
    855                         kf.setValueWasSetOnStart(true);
    856                     } catch (InvocationTargetException e) {
    857                         Log.e("PropertyValuesHolder", e.toString());
    858                     } catch (IllegalAccessException e) {
    859                         Log.e("PropertyValuesHolder", e.toString());
    860                     }
    861                 }
    862             }
    863         }
    864     }
    865 
    866     private Object convertBack(Object value) {
    867         if (mConverter != null) {
    868             if (!(mConverter instanceof BidirectionalTypeConverter)) {
    869                 throw new IllegalArgumentException("Converter "
    870                         + mConverter.getClass().getName()
    871                         + " must be a BidirectionalTypeConverter");
    872             }
    873             value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
    874         }
    875         return value;
    876     }
    877 
    878     /**
    879      * Utility function to set the value stored in a particular Keyframe. The value used is
    880      * whatever the value is for the property name specified in the keyframe on the target object.
    881      *
    882      * @param target The target object from which the current value should be extracted.
    883      * @param kf The keyframe which holds the property name and value.
    884      */
    885     private void setupValue(Object target, Keyframe kf) {
    886         if (mProperty != null) {
    887             Object value = convertBack(mProperty.get(target));
    888             kf.setValue(value);
    889         } else {
    890             try {
    891                 if (mGetter == null) {
    892                     Class targetClass = target.getClass();
    893                     setupGetter(targetClass);
    894                     if (mGetter == null) {
    895                         // Already logged the error - just return to avoid NPE
    896                         return;
    897                     }
    898                 }
    899                 Object value = convertBack(mGetter.invoke(target));
    900                 kf.setValue(value);
    901             } catch (InvocationTargetException e) {
    902                 Log.e("PropertyValuesHolder", e.toString());
    903             } catch (IllegalAccessException e) {
    904                 Log.e("PropertyValuesHolder", e.toString());
    905             }
    906         }
    907     }
    908 
    909     /**
    910      * This function is called by ObjectAnimator when setting the start values for an animation.
    911      * The start values are set according to the current values in the target object. The
    912      * property whose value is extracted is whatever is specified by the propertyName of this
    913      * PropertyValuesHolder object.
    914      *
    915      * @param target The object which holds the start values that should be set.
    916      */
    917     void setupStartValue(Object target) {
    918         List<Keyframe> keyframes = mKeyframes.getKeyframes();
    919         if (!keyframes.isEmpty()) {
    920             setupValue(target, keyframes.get(0));
    921         }
    922     }
    923 
    924     /**
    925      * This function is called by ObjectAnimator when setting the end values for an animation.
    926      * The end values are set according to the current values in the target object. The
    927      * property whose value is extracted is whatever is specified by the propertyName of this
    928      * PropertyValuesHolder object.
    929      *
    930      * @param target The object which holds the start values that should be set.
    931      */
    932     void setupEndValue(Object target) {
    933         List<Keyframe> keyframes = mKeyframes.getKeyframes();
    934         if (!keyframes.isEmpty()) {
    935             setupValue(target, keyframes.get(keyframes.size() - 1));
    936         }
    937     }
    938 
    939     @Override
    940     public PropertyValuesHolder clone() {
    941         try {
    942             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
    943             newPVH.mPropertyName = mPropertyName;
    944             newPVH.mProperty = mProperty;
    945             newPVH.mKeyframes = mKeyframes.clone();
    946             newPVH.mEvaluator = mEvaluator;
    947             return newPVH;
    948         } catch (CloneNotSupportedException e) {
    949             // won't reach here
    950             return null;
    951         }
    952     }
    953 
    954     /**
    955      * Internal function to set the value on the target object, using the setter set up
    956      * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
    957      * to handle turning the value calculated by ValueAnimator into a value set on the object
    958      * according to the name of the property.
    959      * @param target The target object on which the value is set
    960      */
    961     void setAnimatedValue(Object target) {
    962         if (mProperty != null) {
    963             mProperty.set(target, getAnimatedValue());
    964         }
    965         if (mSetter != null) {
    966             try {
    967                 mTmpValueArray[0] = getAnimatedValue();
    968                 mSetter.invoke(target, mTmpValueArray);
    969             } catch (InvocationTargetException e) {
    970                 Log.e("PropertyValuesHolder", e.toString());
    971             } catch (IllegalAccessException e) {
    972                 Log.e("PropertyValuesHolder", e.toString());
    973             }
    974         }
    975     }
    976 
    977     /**
    978      * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
    979      * to calculate animated values.
    980      */
    981     void init() {
    982         if (mEvaluator == null) {
    983             // We already handle int and float automatically, but not their Object
    984             // equivalents
    985             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
    986                     (mValueType == Float.class) ? sFloatEvaluator :
    987                     null;
    988         }
    989         if (mEvaluator != null) {
    990             // KeyframeSet knows how to evaluate the common types - only give it a custom
    991             // evaluator if one has been set on this class
    992             mKeyframes.setEvaluator(mEvaluator);
    993         }
    994     }
    995 
    996     /**
    997      * The TypeEvaluator will be automatically determined based on the type of values
    998      * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
    999      * desired. This may be important in cases where either the type of the values supplied
   1000      * do not match the way that they should be interpolated between, or if the values
   1001      * are of a custom type or one not currently understood by the animation system. Currently,
   1002      * only values of type float and int (and their Object equivalents: Float
   1003      * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
   1004      * @param evaluator
   1005      */
   1006     public void setEvaluator(TypeEvaluator evaluator) {
   1007         mEvaluator = evaluator;
   1008         mKeyframes.setEvaluator(evaluator);
   1009     }
   1010 
   1011     /**
   1012      * Function used to calculate the value according to the evaluator set up for
   1013      * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
   1014      *
   1015      * @param fraction The elapsed, interpolated fraction of the animation.
   1016      */
   1017     void calculateValue(float fraction) {
   1018         Object value = mKeyframes.getValue(fraction);
   1019         mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
   1020     }
   1021 
   1022     /**
   1023      * Sets the name of the property that will be animated. This name is used to derive
   1024      * a setter function that will be called to set animated values.
   1025      * For example, a property name of <code>foo</code> will result
   1026      * in a call to the function <code>setFoo()</code> on the target object. If either
   1027      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
   1028      * also be derived and called.
   1029      *
   1030      * <p>Note that the setter function derived from this property name
   1031      * must take the same parameter type as the
   1032      * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
   1033      * the setter function will fail.</p>
   1034      *
   1035      * @param propertyName The name of the property being animated.
   1036      */
   1037     public void setPropertyName(String propertyName) {
   1038         mPropertyName = propertyName;
   1039     }
   1040 
   1041     /**
   1042      * Sets the property that will be animated.
   1043      *
   1044      * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
   1045      * must exist on the target object specified in that ObjectAnimator.</p>
   1046      *
   1047      * @param property The property being animated.
   1048      */
   1049     public void setProperty(Property property) {
   1050         mProperty = property;
   1051     }
   1052 
   1053     /**
   1054      * Gets the name of the property that will be animated. This name will be used to derive
   1055      * a setter function that will be called to set animated values.
   1056      * For example, a property name of <code>foo</code> will result
   1057      * in a call to the function <code>setFoo()</code> on the target object. If either
   1058      * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
   1059      * also be derived and called.
   1060      */
   1061     public String getPropertyName() {
   1062         return mPropertyName;
   1063     }
   1064 
   1065     /**
   1066      * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
   1067      * most recently calculated in calculateValue().
   1068      * @return
   1069      */
   1070     Object getAnimatedValue() {
   1071         return mAnimatedValue;
   1072     }
   1073 
   1074     /**
   1075      * PropertyValuesHolder is Animators use to hold internal animation related data.
   1076      * Therefore, in order to replicate the animation behavior, we need to get data out of
   1077      * PropertyValuesHolder.
   1078      * @hide
   1079      */
   1080     public void getPropertyValues(PropertyValues values) {
   1081         init();
   1082         values.propertyName = mPropertyName;
   1083         values.type = mValueType;
   1084         values.startValue = mKeyframes.getValue(0);
   1085         if (values.startValue instanceof PathParser.PathData) {
   1086             // PathData evaluator returns the same mutable PathData object when query fraction,
   1087             // so we have to make a copy here.
   1088             values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
   1089         }
   1090         values.endValue = mKeyframes.getValue(1);
   1091         if (values.endValue instanceof PathParser.PathData) {
   1092             // PathData evaluator returns the same mutable PathData object when query fraction,
   1093             // so we have to make a copy here.
   1094             values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
   1095         }
   1096         // TODO: We need a better way to get data out of keyframes.
   1097         if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
   1098                 || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
   1099             // property values will animate based on external data source (e.g. Path)
   1100             values.dataSource = new PropertyValues.DataSource() {
   1101                 @Override
   1102                 public Object getValueAtFraction(float fraction) {
   1103                     return mKeyframes.getValue(fraction);
   1104                 }
   1105             };
   1106         } else {
   1107             values.dataSource = null;
   1108         }
   1109     }
   1110 
   1111     @Override
   1112     public String toString() {
   1113         return mPropertyName + ": " + mKeyframes.toString();
   1114     }
   1115 
   1116     /**
   1117      * Utility method to derive a setter/getter method name from a property name, where the
   1118      * prefix is typically "set" or "get" and the first letter of the property name is
   1119      * capitalized.
   1120      *
   1121      * @param prefix The precursor to the method name, before the property name begins, typically
   1122      * "set" or "get".
   1123      * @param propertyName The name of the property that represents the bulk of the method name
   1124      * after the prefix. The first letter of this word will be capitalized in the resulting
   1125      * method name.
   1126      * @return String the property name converted to a method name according to the conventions
   1127      * specified above.
   1128      */
   1129     static String getMethodName(String prefix, String propertyName) {
   1130         if (propertyName == null || propertyName.length() == 0) {
   1131             // shouldn't get here
   1132             return prefix;
   1133         }
   1134         char firstLetter = Character.toUpperCase(propertyName.charAt(0));
   1135         String theRest = propertyName.substring(1);
   1136         return prefix + firstLetter + theRest;
   1137     }
   1138 
   1139     static class IntPropertyValuesHolder extends PropertyValuesHolder {
   1140 
   1141         // Cache JNI functions to avoid looking them up twice
   1142         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1143                 new HashMap<Class, HashMap<String, Long>>();
   1144         long mJniSetter;
   1145         private IntProperty mIntProperty;
   1146 
   1147         Keyframes.IntKeyframes mIntKeyframes;
   1148         int mIntAnimatedValue;
   1149 
   1150         public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
   1151             super(propertyName);
   1152             mValueType = int.class;
   1153             mKeyframes = keyframes;
   1154             mIntKeyframes = keyframes;
   1155         }
   1156 
   1157         public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) {
   1158             super(property);
   1159             mValueType = int.class;
   1160             mKeyframes = keyframes;
   1161             mIntKeyframes = keyframes;
   1162             if (property instanceof  IntProperty) {
   1163                 mIntProperty = (IntProperty) mProperty;
   1164             }
   1165         }
   1166 
   1167         public IntPropertyValuesHolder(String propertyName, int... values) {
   1168             super(propertyName);
   1169             setIntValues(values);
   1170         }
   1171 
   1172         public IntPropertyValuesHolder(Property property, int... values) {
   1173             super(property);
   1174             setIntValues(values);
   1175             if (property instanceof  IntProperty) {
   1176                 mIntProperty = (IntProperty) mProperty;
   1177             }
   1178         }
   1179 
   1180         @Override
   1181         public void setIntValues(int... values) {
   1182             super.setIntValues(values);
   1183             mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
   1184         }
   1185 
   1186         @Override
   1187         void calculateValue(float fraction) {
   1188             mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
   1189         }
   1190 
   1191         @Override
   1192         Object getAnimatedValue() {
   1193             return mIntAnimatedValue;
   1194         }
   1195 
   1196         @Override
   1197         public IntPropertyValuesHolder clone() {
   1198             IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
   1199             newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes;
   1200             return newPVH;
   1201         }
   1202 
   1203         /**
   1204          * Internal function to set the value on the target object, using the setter set up
   1205          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1206          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1207          * according to the name of the property.
   1208          * @param target The target object on which the value is set
   1209          */
   1210         @Override
   1211         void setAnimatedValue(Object target) {
   1212             if (mIntProperty != null) {
   1213                 mIntProperty.setValue(target, mIntAnimatedValue);
   1214                 return;
   1215             }
   1216             if (mProperty != null) {
   1217                 mProperty.set(target, mIntAnimatedValue);
   1218                 return;
   1219             }
   1220             if (mJniSetter != 0) {
   1221                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
   1222                 return;
   1223             }
   1224             if (mSetter != null) {
   1225                 try {
   1226                     mTmpValueArray[0] = mIntAnimatedValue;
   1227                     mSetter.invoke(target, mTmpValueArray);
   1228                 } catch (InvocationTargetException e) {
   1229                     Log.e("PropertyValuesHolder", e.toString());
   1230                 } catch (IllegalAccessException e) {
   1231                     Log.e("PropertyValuesHolder", e.toString());
   1232                 }
   1233             }
   1234         }
   1235 
   1236         @Override
   1237         void setupSetter(Class targetClass) {
   1238             if (mProperty != null) {
   1239                 return;
   1240             }
   1241             // Check new static hashmap<propName, int> for setter method
   1242             synchronized(sJNISetterPropertyMap) {
   1243                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1244                 boolean wasInMap = false;
   1245                 if (propertyMap != null) {
   1246                     wasInMap = propertyMap.containsKey(mPropertyName);
   1247                     if (wasInMap) {
   1248                         Long jniSetter = propertyMap.get(mPropertyName);
   1249                         if (jniSetter != null) {
   1250                             mJniSetter = jniSetter;
   1251                         }
   1252                     }
   1253                 }
   1254                 if (!wasInMap) {
   1255                     String methodName = getMethodName("set", mPropertyName);
   1256                     try {
   1257                         mJniSetter = nGetIntMethod(targetClass, methodName);
   1258                     } catch (NoSuchMethodError e) {
   1259                         // Couldn't find it via JNI - try reflection next. Probably means the method
   1260                         // doesn't exist, or the type is wrong. An error will be logged later if
   1261                         // reflection fails as well.
   1262                     }
   1263                     if (propertyMap == null) {
   1264                         propertyMap = new HashMap<String, Long>();
   1265                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1266                     }
   1267                     propertyMap.put(mPropertyName, mJniSetter);
   1268                 }
   1269             }
   1270             if (mJniSetter == 0) {
   1271                 // Couldn't find method through fast JNI approach - just use reflection
   1272                 super.setupSetter(targetClass);
   1273             }
   1274         }
   1275     }
   1276 
   1277     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
   1278 
   1279         // Cache JNI functions to avoid looking them up twice
   1280         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1281                 new HashMap<Class, HashMap<String, Long>>();
   1282         long mJniSetter;
   1283         private FloatProperty mFloatProperty;
   1284 
   1285         Keyframes.FloatKeyframes mFloatKeyframes;
   1286         float mFloatAnimatedValue;
   1287 
   1288         public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
   1289             super(propertyName);
   1290             mValueType = float.class;
   1291             mKeyframes = keyframes;
   1292             mFloatKeyframes = keyframes;
   1293         }
   1294 
   1295         public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) {
   1296             super(property);
   1297             mValueType = float.class;
   1298             mKeyframes = keyframes;
   1299             mFloatKeyframes = keyframes;
   1300             if (property instanceof FloatProperty) {
   1301                 mFloatProperty = (FloatProperty) mProperty;
   1302             }
   1303         }
   1304 
   1305         public FloatPropertyValuesHolder(String propertyName, float... values) {
   1306             super(propertyName);
   1307             setFloatValues(values);
   1308         }
   1309 
   1310         public FloatPropertyValuesHolder(Property property, float... values) {
   1311             super(property);
   1312             setFloatValues(values);
   1313             if (property instanceof  FloatProperty) {
   1314                 mFloatProperty = (FloatProperty) mProperty;
   1315             }
   1316         }
   1317 
   1318         @Override
   1319         public void setFloatValues(float... values) {
   1320             super.setFloatValues(values);
   1321             mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
   1322         }
   1323 
   1324         @Override
   1325         void calculateValue(float fraction) {
   1326             mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
   1327         }
   1328 
   1329         @Override
   1330         Object getAnimatedValue() {
   1331             return mFloatAnimatedValue;
   1332         }
   1333 
   1334         @Override
   1335         public FloatPropertyValuesHolder clone() {
   1336             FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
   1337             newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes;
   1338             return newPVH;
   1339         }
   1340 
   1341         /**
   1342          * Internal function to set the value on the target object, using the setter set up
   1343          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1344          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1345          * according to the name of the property.
   1346          * @param target The target object on which the value is set
   1347          */
   1348         @Override
   1349         void setAnimatedValue(Object target) {
   1350             if (mFloatProperty != null) {
   1351                 mFloatProperty.setValue(target, mFloatAnimatedValue);
   1352                 return;
   1353             }
   1354             if (mProperty != null) {
   1355                 mProperty.set(target, mFloatAnimatedValue);
   1356                 return;
   1357             }
   1358             if (mJniSetter != 0) {
   1359                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
   1360                 return;
   1361             }
   1362             if (mSetter != null) {
   1363                 try {
   1364                     mTmpValueArray[0] = mFloatAnimatedValue;
   1365                     mSetter.invoke(target, mTmpValueArray);
   1366                 } catch (InvocationTargetException e) {
   1367                     Log.e("PropertyValuesHolder", e.toString());
   1368                 } catch (IllegalAccessException e) {
   1369                     Log.e("PropertyValuesHolder", e.toString());
   1370                 }
   1371             }
   1372         }
   1373 
   1374         @Override
   1375         void setupSetter(Class targetClass) {
   1376             if (mProperty != null) {
   1377                 return;
   1378             }
   1379             // Check new static hashmap<propName, int> for setter method
   1380             synchronized (sJNISetterPropertyMap) {
   1381                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1382                 boolean wasInMap = false;
   1383                 if (propertyMap != null) {
   1384                     wasInMap = propertyMap.containsKey(mPropertyName);
   1385                     if (wasInMap) {
   1386                         Long jniSetter = propertyMap.get(mPropertyName);
   1387                         if (jniSetter != null) {
   1388                             mJniSetter = jniSetter;
   1389                         }
   1390                     }
   1391                 }
   1392                 if (!wasInMap) {
   1393                     String methodName = getMethodName("set", mPropertyName);
   1394                     try {
   1395                         mJniSetter = nGetFloatMethod(targetClass, methodName);
   1396                     } catch (NoSuchMethodError e) {
   1397                         // Couldn't find it via JNI - try reflection next. Probably means the method
   1398                         // doesn't exist, or the type is wrong. An error will be logged later if
   1399                         // reflection fails as well.
   1400                     }
   1401                     if (propertyMap == null) {
   1402                         propertyMap = new HashMap<String, Long>();
   1403                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1404                     }
   1405                     propertyMap.put(mPropertyName, mJniSetter);
   1406                 }
   1407             }
   1408             if (mJniSetter == 0) {
   1409                 // Couldn't find method through fast JNI approach - just use reflection
   1410                 super.setupSetter(targetClass);
   1411             }
   1412         }
   1413 
   1414     }
   1415 
   1416     static class MultiFloatValuesHolder extends PropertyValuesHolder {
   1417         private long mJniSetter;
   1418         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1419                 new HashMap<Class, HashMap<String, Long>>();
   1420 
   1421         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
   1422                 TypeEvaluator evaluator, Object... values) {
   1423             super(propertyName);
   1424             setConverter(converter);
   1425             setObjectValues(values);
   1426             setEvaluator(evaluator);
   1427         }
   1428 
   1429         public MultiFloatValuesHolder(String propertyName, TypeConverter converter,
   1430                 TypeEvaluator evaluator, Keyframes keyframes) {
   1431             super(propertyName);
   1432             setConverter(converter);
   1433             mKeyframes = keyframes;
   1434             setEvaluator(evaluator);
   1435         }
   1436 
   1437         /**
   1438          * Internal function to set the value on the target object, using the setter set up
   1439          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1440          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1441          * according to the name of the property.
   1442          *
   1443          * @param target The target object on which the value is set
   1444          */
   1445         @Override
   1446         void setAnimatedValue(Object target) {
   1447             float[] values = (float[]) getAnimatedValue();
   1448             int numParameters = values.length;
   1449             if (mJniSetter != 0) {
   1450                 switch (numParameters) {
   1451                     case 1:
   1452                         nCallFloatMethod(target, mJniSetter, values[0]);
   1453                         break;
   1454                     case 2:
   1455                         nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]);
   1456                         break;
   1457                     case 4:
   1458                         nCallFourFloatMethod(target, mJniSetter, values[0], values[1],
   1459                                 values[2], values[3]);
   1460                         break;
   1461                     default: {
   1462                         nCallMultipleFloatMethod(target, mJniSetter, values);
   1463                         break;
   1464                     }
   1465                 }
   1466             }
   1467         }
   1468 
   1469         /**
   1470          * Internal function (called from ObjectAnimator) to set up the setter and getter
   1471          * prior to running the animation. No getter can be used for multiple parameters.
   1472          *
   1473          * @param target The object on which the setter exists.
   1474          */
   1475         @Override
   1476         void setupSetterAndGetter(Object target) {
   1477             setupSetter(target.getClass());
   1478         }
   1479 
   1480         @Override
   1481         void setupSetter(Class targetClass) {
   1482             if (mJniSetter != 0) {
   1483                 return;
   1484             }
   1485             synchronized(sJNISetterPropertyMap) {
   1486                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1487                 boolean wasInMap = false;
   1488                 if (propertyMap != null) {
   1489                     wasInMap = propertyMap.containsKey(mPropertyName);
   1490                     if (wasInMap) {
   1491                         Long jniSetter = propertyMap.get(mPropertyName);
   1492                         if (jniSetter != null) {
   1493                             mJniSetter = jniSetter;
   1494                         }
   1495                     }
   1496                 }
   1497                 if (!wasInMap) {
   1498                     String methodName = getMethodName("set", mPropertyName);
   1499                     calculateValue(0f);
   1500                     float[] values = (float[]) getAnimatedValue();
   1501                     int numParams = values.length;
   1502                     try {
   1503                         mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
   1504                     } catch (NoSuchMethodError e) {
   1505                         // try without the 'set' prefix
   1506                         try {
   1507                             mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
   1508                                     numParams);
   1509                         } catch (NoSuchMethodError e2) {
   1510                             // just try reflection next
   1511                         }
   1512                     }
   1513                     if (propertyMap == null) {
   1514                         propertyMap = new HashMap<String, Long>();
   1515                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1516                     }
   1517                     propertyMap.put(mPropertyName, mJniSetter);
   1518                 }
   1519            }
   1520         }
   1521     }
   1522 
   1523     static class MultiIntValuesHolder extends PropertyValuesHolder {
   1524         private long mJniSetter;
   1525         private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap =
   1526                 new HashMap<Class, HashMap<String, Long>>();
   1527 
   1528         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
   1529                 TypeEvaluator evaluator, Object... values) {
   1530             super(propertyName);
   1531             setConverter(converter);
   1532             setObjectValues(values);
   1533             setEvaluator(evaluator);
   1534         }
   1535 
   1536         public MultiIntValuesHolder(String propertyName, TypeConverter converter,
   1537                 TypeEvaluator evaluator, Keyframes keyframes) {
   1538             super(propertyName);
   1539             setConverter(converter);
   1540             mKeyframes = keyframes;
   1541             setEvaluator(evaluator);
   1542         }
   1543 
   1544         /**
   1545          * Internal function to set the value on the target object, using the setter set up
   1546          * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
   1547          * to handle turning the value calculated by ValueAnimator into a value set on the object
   1548          * according to the name of the property.
   1549          *
   1550          * @param target The target object on which the value is set
   1551          */
   1552         @Override
   1553         void setAnimatedValue(Object target) {
   1554             int[] values = (int[]) getAnimatedValue();
   1555             int numParameters = values.length;
   1556             if (mJniSetter != 0) {
   1557                 switch (numParameters) {
   1558                     case 1:
   1559                         nCallIntMethod(target, mJniSetter, values[0]);
   1560                         break;
   1561                     case 2:
   1562                         nCallTwoIntMethod(target, mJniSetter, values[0], values[1]);
   1563                         break;
   1564                     case 4:
   1565                         nCallFourIntMethod(target, mJniSetter, values[0], values[1],
   1566                                 values[2], values[3]);
   1567                         break;
   1568                     default: {
   1569                         nCallMultipleIntMethod(target, mJniSetter, values);
   1570                         break;
   1571                     }
   1572                 }
   1573             }
   1574         }
   1575 
   1576         /**
   1577          * Internal function (called from ObjectAnimator) to set up the setter and getter
   1578          * prior to running the animation. No getter can be used for multiple parameters.
   1579          *
   1580          * @param target The object on which the setter exists.
   1581          */
   1582         @Override
   1583         void setupSetterAndGetter(Object target) {
   1584             setupSetter(target.getClass());
   1585         }
   1586 
   1587         @Override
   1588         void setupSetter(Class targetClass) {
   1589             if (mJniSetter != 0) {
   1590                 return;
   1591             }
   1592             synchronized(sJNISetterPropertyMap) {
   1593                 HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
   1594                 boolean wasInMap = false;
   1595                 if (propertyMap != null) {
   1596                     wasInMap = propertyMap.containsKey(mPropertyName);
   1597                     if (wasInMap) {
   1598                         Long jniSetter = propertyMap.get(mPropertyName);
   1599                         if (jniSetter != null) {
   1600                             mJniSetter = jniSetter;
   1601                         }
   1602                     }
   1603                 }
   1604                 if (!wasInMap) {
   1605                     String methodName = getMethodName("set", mPropertyName);
   1606                     calculateValue(0f);
   1607                     int[] values = (int[]) getAnimatedValue();
   1608                     int numParams = values.length;
   1609                     try {
   1610                         mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams);
   1611                     } catch (NoSuchMethodError e) {
   1612                         // try without the 'set' prefix
   1613                         try {
   1614                             mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName,
   1615                                     numParams);
   1616                         } catch (NoSuchMethodError e2) {
   1617                             // couldn't find it.
   1618                         }
   1619                     }
   1620                     if (propertyMap == null) {
   1621                         propertyMap = new HashMap<String, Long>();
   1622                         sJNISetterPropertyMap.put(targetClass, propertyMap);
   1623                     }
   1624                     propertyMap.put(mPropertyName, mJniSetter);
   1625                 }
   1626             }
   1627         }
   1628     }
   1629 
   1630     /**
   1631      * Convert from PointF to float[] for multi-float setters along a Path.
   1632      */
   1633     private static class PointFToFloatArray extends TypeConverter<PointF, float[]> {
   1634         private float[] mCoordinates = new float[2];
   1635 
   1636         public PointFToFloatArray() {
   1637             super(PointF.class, float[].class);
   1638         }
   1639 
   1640         @Override
   1641         public float[] convert(PointF value) {
   1642             mCoordinates[0] = value.x;
   1643             mCoordinates[1] = value.y;
   1644             return mCoordinates;
   1645         }
   1646     };
   1647 
   1648     /**
   1649      * Convert from PointF to int[] for multi-int setters along a Path.
   1650      */
   1651     private static class PointFToIntArray extends TypeConverter<PointF, int[]> {
   1652         private int[] mCoordinates = new int[2];
   1653 
   1654         public PointFToIntArray() {
   1655             super(PointF.class, int[].class);
   1656         }
   1657 
   1658         @Override
   1659         public int[] convert(PointF value) {
   1660             mCoordinates[0] = Math.round(value.x);
   1661             mCoordinates[1] = Math.round(value.y);
   1662             return mCoordinates;
   1663         }
   1664     };
   1665 
   1666     /**
   1667      * @hide
   1668      */
   1669     public static class PropertyValues {
   1670         public String propertyName;
   1671         public Class type;
   1672         public Object startValue;
   1673         public Object endValue;
   1674         public DataSource dataSource = null;
   1675         public interface DataSource {
   1676             Object getValueAtFraction(float fraction);
   1677         }
   1678         public String toString() {
   1679             return ("property name: " + propertyName + ", type: " + type + ", startValue: "
   1680                     + startValue.toString() + ", endValue: " + endValue.toString());
   1681         }
   1682     }
   1683 
   1684     native static private long nGetIntMethod(Class targetClass, String methodName);
   1685     native static private long nGetFloatMethod(Class targetClass, String methodName);
   1686     native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
   1687             int numParams);
   1688     native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
   1689             int numParams);
   1690     native static private void nCallIntMethod(Object target, long methodID, int arg);
   1691     native static private void nCallFloatMethod(Object target, long methodID, float arg);
   1692     native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
   1693     native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
   1694             int arg3, int arg4);
   1695     native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
   1696     native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
   1697             float arg2);
   1698     native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
   1699             float arg2, float arg3, float arg4);
   1700     native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
   1701 }
   1702