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      */
PropertyValuesHolder(String propertyName)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      */
PropertyValuesHolder(Property property)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      */
ofInt(String propertyName, int... values)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      */
ofInt(Property<?, Integer> property, int... values)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      */
ofMultiInt(String propertyName, int[][] values)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      */
ofMultiInt(String propertyName, Path path)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
ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)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      */
ofMultiInt(String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)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      */
ofFloat(String propertyName, float... values)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      */
ofFloat(Property<?, Float> property, float... values)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      */
ofMultiFloat(String propertyName, float[][] values)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      */
ofMultiFloat(String propertyName, Path path)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
ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)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      */
ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)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      */
ofObject(String propertyName, TypeEvaluator evaluator, Object... values)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      */
ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)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
ofObject(Property property, TypeEvaluator<V> evaluator, V... values)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
ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)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      */
ofObject(Property<?, V> property, TypeConverter<PointF, V> converter, Path path)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      */
ofKeyframe(String propertyName, Keyframe... values)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      */
ofKeyframe(Property property, Keyframe... values)561     public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
562         KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
563         return ofKeyframes(property, keyframeSet);
564     }
565 
ofKeyframes(String propertyName, Keyframes keyframes)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 
ofKeyframes(Property property, Keyframes keyframes)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      */
setIntValues(int... values)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      */
setFloatValues(float... values)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      */
setKeyframes(Keyframe... values)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      */
setObjectValues(Object... values)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      */
setConverter(TypeConverter converter)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      */
getPropertyFunction(Class targetClass, String prefix, Class valueType)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      */
setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType)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      */
setupSetter(Class targetClass)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      */
setupGetter(Class targetClass)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      */
setupSetterAndGetter(Object target)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 
convertBack(Object value)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      */
setupValue(Object target, Keyframe kf)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      */
setupStartValue(Object target)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      */
setupEndValue(Object target)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
clone()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      */
setAnimatedValue(Object target)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      */
init()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      */
setEvaluator(TypeEvaluator evaluator)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      */
calculateValue(float fraction)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      */
setPropertyName(String propertyName)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      */
setProperty(Property property)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      */
getPropertyName()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      */
getAnimatedValue()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      */
getPropertyValues(PropertyValues values)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      */
getValueType()1117     public Class getValueType() {
1118         return mValueType;
1119     }
1120 
1121     @Override
toString()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      */
getMethodName(String prefix, String propertyName)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 
IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes)1160         public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) {
1161             super(propertyName);
1162             mValueType = int.class;
1163             mKeyframes = keyframes;
1164             mIntKeyframes = keyframes;
1165         }
1166 
IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes)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 
IntPropertyValuesHolder(String propertyName, int... values)1177         public IntPropertyValuesHolder(String propertyName, int... values) {
1178             super(propertyName);
1179             setIntValues(values);
1180         }
1181 
IntPropertyValuesHolder(Property property, int... values)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
setProperty(Property property)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
setIntValues(int... values)1200         public void setIntValues(int... values) {
1201             super.setIntValues(values);
1202             mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
1203         }
1204 
1205         @Override
calculateValue(float fraction)1206         void calculateValue(float fraction) {
1207             mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
1208         }
1209 
1210         @Override
getAnimatedValue()1211         Object getAnimatedValue() {
1212             return mIntAnimatedValue;
1213         }
1214 
1215         @Override
clone()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
setAnimatedValue(Object target)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
setupSetter(Class targetClass)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 
FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes)1307         public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) {
1308             super(propertyName);
1309             mValueType = float.class;
1310             mKeyframes = keyframes;
1311             mFloatKeyframes = keyframes;
1312         }
1313 
FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes)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 
FloatPropertyValuesHolder(String propertyName, float... values)1324         public FloatPropertyValuesHolder(String propertyName, float... values) {
1325             super(propertyName);
1326             setFloatValues(values);
1327         }
1328 
FloatPropertyValuesHolder(Property property, float... values)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
setProperty(Property property)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
setFloatValues(float... values)1347         public void setFloatValues(float... values) {
1348             super.setFloatValues(values);
1349             mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
1350         }
1351 
1352         @Override
calculateValue(float fraction)1353         void calculateValue(float fraction) {
1354             mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
1355         }
1356 
1357         @Override
getAnimatedValue()1358         Object getAnimatedValue() {
1359             return mFloatAnimatedValue;
1360         }
1361 
1362         @Override
clone()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
setAnimatedValue(Object target)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
setupSetter(Class targetClass)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 
MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)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 
MultiFloatValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)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
setAnimatedValue(Object target)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
setupSetterAndGetter(Object target)1504         void setupSetterAndGetter(Object target) {
1505             setupSetter(target.getClass());
1506         }
1507 
1508         @Override
setupSetter(Class targetClass)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 
MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Object... values)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 
MultiIntValuesHolder(String propertyName, TypeConverter converter, TypeEvaluator evaluator, Keyframes keyframes)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
setAnimatedValue(Object target)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
setupSetterAndGetter(Object target)1611         void setupSetterAndGetter(Object target) {
1612             setupSetter(target.getClass());
1613         }
1614 
1615         @Override
setupSetter(Class targetClass)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 
PointFToFloatArray()1664         public PointFToFloatArray() {
1665             super(PointF.class, float[].class);
1666         }
1667 
1668         @Override
convert(PointF value)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 
PointFToIntArray()1682         public PointFToIntArray() {
1683             super(PointF.class, int[].class);
1684         }
1685 
1686         @Override
convert(PointF value)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 {
getValueAtFraction(float fraction)1704             Object getValueAtFraction(float fraction);
1705         }
toString()1706         public String toString() {
1707             return ("property name: " + propertyName + ", type: " + type + ", startValue: "
1708                     + startValue.toString() + ", endValue: " + endValue.toString());
1709         }
1710     }
1711 
nGetIntMethod(Class targetClass, String methodName)1712     native static private long nGetIntMethod(Class targetClass, String methodName);
nGetFloatMethod(Class targetClass, String methodName)1713     native static private long nGetFloatMethod(Class targetClass, String methodName);
nGetMultipleIntMethod(Class targetClass, String methodName, int numParams)1714     native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
1715             int numParams);
nGetMultipleFloatMethod(Class targetClass, String methodName, int numParams)1716     native static private long nGetMultipleFloatMethod(Class targetClass, String methodName,
1717             int numParams);
nCallIntMethod(Object target, long methodID, int arg)1718     native static private void nCallIntMethod(Object target, long methodID, int arg);
nCallFloatMethod(Object target, long methodID, float arg)1719     native static private void nCallFloatMethod(Object target, long methodID, float arg);
nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)1720     native static private void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)1721     native static private void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2,
1722             int arg3, int arg4);
nCallMultipleIntMethod(Object target, long methodID, int[] args)1723     native static private void nCallMultipleIntMethod(Object target, long methodID, int[] args);
nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)1724     native static private void nCallTwoFloatMethod(Object target, long methodID, float arg1,
1725             float arg2);
nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)1726     native static private void nCallFourFloatMethod(Object target, long methodID, float arg1,
1727             float arg2, float arg3, float arg4);
nCallMultipleFloatMethod(Object target, long methodID, float[] args)1728     native static private void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
1729 }
1730