• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package android.graphics.drawable;
16 
17 import android.animation.Animator;
18 import android.animation.Animator.AnimatorListener;
19 import android.animation.AnimatorInflater;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.animation.PropertyValuesHolder;
24 import android.animation.TimeInterpolator;
25 import android.animation.ValueAnimator;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.ActivityThread;
29 import android.app.Application;
30 import android.content.pm.ActivityInfo.Config;
31 import android.content.res.ColorStateList;
32 import android.content.res.Resources;
33 import android.content.res.Resources.Theme;
34 import android.content.res.TypedArray;
35 import android.graphics.Canvas;
36 import android.graphics.ColorFilter;
37 import android.graphics.Insets;
38 import android.graphics.Outline;
39 import android.graphics.PixelFormat;
40 import android.graphics.PorterDuff;
41 import android.graphics.Rect;
42 import android.os.Build;
43 import android.util.ArrayMap;
44 import android.util.AttributeSet;
45 import android.util.IntArray;
46 import android.util.Log;
47 import android.util.LongArray;
48 import android.util.PathParser;
49 import android.util.Property;
50 import android.util.TimeUtils;
51 import android.view.Choreographer;
52 import android.view.DisplayListCanvas;
53 import android.view.RenderNode;
54 import android.view.RenderNodeAnimatorSetHelper;
55 import android.view.View;
56 
57 import com.android.internal.R;
58 import com.android.internal.util.VirtualRefBasePtr;
59 
60 import org.xmlpull.v1.XmlPullParser;
61 import org.xmlpull.v1.XmlPullParserException;
62 
63 import java.io.IOException;
64 import java.lang.ref.WeakReference;
65 import java.util.ArrayList;
66 
67 import dalvik.annotation.optimization.FastNative;
68 
69 /**
70  * This class animates properties of a {@link android.graphics.drawable.VectorDrawable} with
71  * animations defined using {@link android.animation.ObjectAnimator} or
72  * {@link android.animation.AnimatorSet}.
73  * <p>
74  * Starting from API 25, AnimatedVectorDrawable runs on RenderThread (as opposed to on UI thread for
75  * earlier APIs). This means animations in AnimatedVectorDrawable can remain smooth even when there
76  * is heavy workload on the UI thread. Note: If the UI thread is unresponsive, RenderThread may
77  * continue animating until the UI thread is capable of pushing another frame. Therefore, it is not
78  * possible to precisely coordinate a RenderThread-enabled AnimatedVectorDrawable with UI thread
79  * animations. Additionally,
80  * {@link android.graphics.drawable.Animatable2.AnimationCallback#onAnimationEnd(Drawable)} will be
81  * called the frame after the AnimatedVectorDrawable finishes on the RenderThread.
82  * </p>
83  * <p>
84  * AnimatedVectorDrawable can be defined in either <a href="#ThreeXML">three separate XML files</a>,
85  * or <a href="#OneXML">one XML</a>.
86  * </p>
87  * <a name="ThreeXML"></a>
88  * <h3>Define an AnimatedVectorDrawable in three separate XML files</h3>
89  * <ul>
90  * <a name="VDExample"></a>
91  * <li><h4>XML for the VectorDrawable containing properties to be animated</h4>
92  * <p>
93  * Animations can be performed on the animatable attributes in
94  * {@link android.graphics.drawable.VectorDrawable}. These attributes will be animated by
95  * {@link android.animation.ObjectAnimator}. The ObjectAnimator's target can be the root element,
96  * a group element or a path element. The targeted elements need to be named uniquely within
97  * the same VectorDrawable. Elements without animation do not need to be named.
98  * </p>
99  * <p>
100  * Here are all the animatable attributes in {@link android.graphics.drawable.VectorDrawable}:
101  * <table border="2" align="center" cellpadding="5">
102  *     <thead>
103  *         <tr>
104  *             <th>Element Name</th>
105  *             <th>Animatable attribute name</th>
106  *         </tr>
107  *     </thead>
108  *     <tr>
109  *         <td>&lt;vector&gt;</td>
110  *         <td>alpha</td>
111  *     </tr>
112  *     <tr>
113  *         <td rowspan="7">&lt;group&gt;</td>
114  *         <td>rotation</td>
115  *     </tr>
116  *     <tr>
117  *         <td>pivotX</td>
118  *     </tr>
119  *     <tr>
120  *         <td>pivotY</td>
121  *     </tr>
122  *     <tr>
123  *         <td>scaleX</td>
124  *     </tr>
125  *     <tr>
126  *         <td>scaleY</td>
127  *     </tr>
128  *     <tr>
129  *         <td>translateX</td>
130  *     </tr>
131  *     <tr>
132  *         <td>translateY</td>
133  *     </tr>
134  *     <tr>
135  *         <td rowspan="8">&lt;path&gt;</td>
136  *         <td>pathData</td>
137  *     </tr>
138  *     <tr>
139  *         <td>fillColor</td>
140  *     </tr>
141  *     <tr>
142  *         <td>strokeColor</td>
143  *     </tr>
144  *     <tr>
145  *         <td>strokeWidth</td>
146  *     </tr>
147  *     <tr>
148  *         <td>strokeAlpha</td>
149  *     </tr>
150  *     <tr>
151  *         <td>fillAlpha</td>
152  *     </tr>
153  *     <tr>
154  *         <td>trimPathStart</td>
155  *     </tr>
156  *     <tr>
157  *         <td>trimPathOffset</td>
158  *     </tr>
159  *     <tr>
160  *         <td>&lt;clip-path&gt;</td>
161  *         <td>pathData</td>
162  *     </tr>
163  * </table>
164  * </p>
165  * Below is an example of a VectorDrawable defined in vectordrawable.xml. This VectorDrawable is
166  * referred to by its file name (not including file suffix) in the
167  * <a href="AVDExample">AnimatedVectorDrawable XML example</a>.
168  * <pre>
169  * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
170  *     android:height=&quot;64dp&quot;
171  *     android:width=&quot;64dp&quot;
172  *     android:viewportHeight=&quot;600&quot;
173  *     android:viewportWidth=&quot;600&quot; &gt;
174  *     &lt;group
175  *         android:name=&quot;rotationGroup&quot;
176  *         android:pivotX=&quot;300.0&quot;
177  *         android:pivotY=&quot;300.0&quot;
178  *         android:rotation=&quot;45.0&quot; &gt;
179  *         &lt;path
180  *             android:name=&quot;v&quot;
181  *             android:fillColor=&quot;#000000&quot;
182  *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
183  *     &lt;/group&gt;
184  * &lt;/vector&gt;
185  * </pre></li>
186  *
187  * <a name="AVDExample"></a>
188  * <li><h4>XML for AnimatedVectorDrawable</h4>
189  * <p>
190  * An AnimatedVectorDrawable element has a VectorDrawable attribute, and one or more target
191  * element(s). The target element can specify its target by android:name attribute, and link the
192  * target with the proper ObjectAnimator or AnimatorSet by android:animation attribute.
193  * </p>
194  * The following code sample defines an AnimatedVectorDrawable. Note that the names refer to the
195  * groups and paths in the <a href="#VDExample">VectorDrawable XML above</a>.
196  * <pre>
197  * &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
198  *     android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
199  *     &lt;target
200  *         android:name=&quot;rotationGroup&quot;
201  *         android:animation=&quot;@anim/rotation&quot; /&gt;
202  *     &lt;target
203  *         android:name=&quot;v&quot;
204  *         android:animation=&quot;@anim/path_morph&quot; /&gt;
205  * &lt;/animated-vector&gt;
206  * </pre>
207  * </li>
208  *
209  * <li><h4>XML for Animations defined using ObjectAnimator or AnimatorSet</h4>
210  * <p>
211  * From the previous <a href="#AVDExample">example of AnimatedVectorDrawable</a>, two animations
212  * were used: rotation.xml and path_morph.xml.
213  * </p>
214  * rotation.xml rotates the target group from 0 degree to 360 degrees over 6000ms:
215  * <pre>
216  * &lt;objectAnimator
217  *     android:duration=&quot;6000&quot;
218  *     android:propertyName=&quot;rotation&quot;
219  *     android:valueFrom=&quot;0&quot;
220  *     android:valueTo=&quot;360&quot; /&gt;
221  * </pre>
222  *
223  * path_morph.xml morphs the path from one shape into the other. Note that the paths must be
224  * compatible for morphing. Specifically, the paths must have the same commands, in the same order,
225  * and must have the same number of parameters for each command. It is recommended to store path
226  * strings as string resources for reuse.
227  * <pre>
228  * &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
229  *     &lt;objectAnimator
230  *         android:duration=&quot;3000&quot;
231  *         android:propertyName=&quot;pathData&quot;
232  *         android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
233  *         android:valueTo=&quot;M300,70 l 0,-70 70,0  0,140 -70,0 z&quot;
234  *         android:valueType=&quot;pathType&quot;/&gt;
235  * &lt;/set&gt;
236  * </pre>
237  * </ul>
238  * <a name="OneXML"></a>
239  * <h3>Define an AnimatedVectorDrawable all in one XML file</h3>
240  * <p>
241  * Since the AAPT tool supports a new format that bundles several related XML files together, we can
242  * merge the XML files from the previous examples into one XML file:
243  * </p>
244  * <pre>
245  * &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
246  *                  xmlns:aapt=&quothttp://schemas.android.com/aapt&quot; &gt;
247  *     &lt;aapt:attr name="android:drawable"&gt;
248  *         &lt;vector
249  *             android:height=&quot;64dp&quot;
250  *             android:width=&quot;64dp&quot;
251  *             android:viewportHeight=&quot;600&quot;
252  *             android:viewportWidth=&quot;600&quot; &gt;
253  *             &lt;group
254  *                 android:name=&quot;rotationGroup&quot;
255  *                 android:pivotX=&quot;300.0&quot;
256  *                 android:pivotY=&quot;300.0&quot;
257  *                 android:rotation=&quot;45.0&quot; &gt;
258  *                 &lt;path
259  *                     android:name=&quot;v&quot;
260  *                     android:fillColor=&quot;#000000&quot;
261  *                     android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
262  *             &lt;/group&gt;
263  *         &lt;/vector&gt;
264  *     &lt;/aapt:attr&gt;
265  *
266  *     &lt;target android:name=&quot;rotationGroup&quot;&gt; *
267  *         &lt;aapt:attr name="android:animation"&gt;
268  *             &lt;objectAnimator
269  *             android:duration=&quot;6000&quot;
270  *             android:propertyName=&quot;rotation&quot;
271  *             android:valueFrom=&quot;0&quot;
272  *             android:valueTo=&quot;360&quot; /&gt;
273  *         &lt;/aapt:attr&gt;
274  *     &lt;/target&gt;
275  *
276  *     &lt;target android:name=&quot;v&quot; &gt;
277  *         &lt;aapt:attr name="android:animation"&gt;
278  *             &lt;set&gt;
279  *                 &lt;objectAnimator
280  *                     android:duration=&quot;3000&quot;
281  *                     android:propertyName=&quot;pathData&quot;
282  *                     android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
283  *                     android:valueTo=&quot;M300,70 l 0,-70 70,0  0,140 -70,0 z&quot;
284  *                     android:valueType=&quot;pathType&quot;/&gt;
285  *             &lt;/set&gt;
286  *         &lt;/aapt:attr&gt;
287  *      &lt;/target&gt;
288  * &lt;/animated-vector&gt;
289  * </pre>
290  *
291  * @attr ref android.R.styleable#AnimatedVectorDrawable_drawable
292  * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name
293  * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_animation
294  */
295 public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
296     private static final String LOGTAG = "AnimatedVectorDrawable";
297 
298     private static final String ANIMATED_VECTOR = "animated-vector";
299     private static final String TARGET = "target";
300 
301     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
302 
303     /** Local, mutable animator set. */
304     private VectorDrawableAnimator mAnimatorSet;
305 
306     /**
307      * The resources against which this drawable was created. Used to attempt
308      * to inflate animators if applyTheme() doesn't get called.
309      */
310     private Resources mRes;
311 
312     private AnimatedVectorDrawableState mAnimatedVectorState;
313 
314     /** The animator set that is parsed from the xml. */
315     private AnimatorSet mAnimatorSetFromXml = null;
316 
317     private boolean mMutated;
318 
319     /** Use a internal AnimatorListener to support callbacks during animation events. */
320     private ArrayList<Animatable2.AnimationCallback> mAnimationCallbacks = null;
321     private AnimatorListener mAnimatorListener = null;
322 
AnimatedVectorDrawable()323     public AnimatedVectorDrawable() {
324         this(null, null);
325     }
326 
AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res)327     private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) {
328         mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
329         mAnimatorSet = new VectorDrawableAnimatorRT(this);
330         mRes = res;
331     }
332 
333     @Override
mutate()334     public Drawable mutate() {
335         if (!mMutated && super.mutate() == this) {
336             mAnimatedVectorState = new AnimatedVectorDrawableState(
337                     mAnimatedVectorState, mCallback, mRes);
338             mMutated = true;
339         }
340         return this;
341     }
342 
343     /**
344      * @hide
345      */
clearMutated()346     public void clearMutated() {
347         super.clearMutated();
348         if (mAnimatedVectorState.mVectorDrawable != null) {
349             mAnimatedVectorState.mVectorDrawable.clearMutated();
350         }
351         mMutated = false;
352     }
353 
354     /**
355      * In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable
356      * animations for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
357      * these animations.
358      *
359      * @return whether invalid animations for vector drawable should be ignored.
360      */
shouldIgnoreInvalidAnimation()361     private static boolean shouldIgnoreInvalidAnimation() {
362         Application app = ActivityThread.currentApplication();
363         if (app == null || app.getApplicationInfo() == null) {
364             return true;
365         }
366         if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
367             return true;
368         }
369         return false;
370     }
371 
372     @Override
getConstantState()373     public ConstantState getConstantState() {
374         mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
375         return mAnimatedVectorState;
376     }
377 
378     @Override
getChangingConfigurations()379     public @Config int getChangingConfigurations() {
380         return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations();
381     }
382 
383     /**
384      * Draws the AnimatedVectorDrawable into the given canvas.
385      * <p>
386      * <strong>Note:</strong> Calling this method with a software canvas when the
387      * AnimatedVectorDrawable is being animated on RenderThread (for API 25 and later) may yield
388      * outdated result, as the UI thread is not guaranteed to be in sync with RenderThread on
389      * VectorDrawable's property changes during RenderThread animations.
390      * </p>
391      *
392      * @param canvas The canvas to draw into
393      */
394     @Override
draw(Canvas canvas)395     public void draw(Canvas canvas) {
396         if (!canvas.isHardwareAccelerated() && mAnimatorSet instanceof VectorDrawableAnimatorRT) {
397             // If we have SW canvas and the RT animation is waiting to start, We need to fallback
398             // to UI thread animation for AVD.
399             if (!mAnimatorSet.isRunning() &&
400                     ((VectorDrawableAnimatorRT) mAnimatorSet).mPendingAnimationActions.size() > 0) {
401                 fallbackOntoUI();
402             }
403         }
404         mAnimatorSet.onDraw(canvas);
405         mAnimatedVectorState.mVectorDrawable.draw(canvas);
406     }
407 
408     @Override
onBoundsChange(Rect bounds)409     protected void onBoundsChange(Rect bounds) {
410         mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
411     }
412 
413     @Override
onStateChange(int[] state)414     protected boolean onStateChange(int[] state) {
415         return mAnimatedVectorState.mVectorDrawable.setState(state);
416     }
417 
418     @Override
onLevelChange(int level)419     protected boolean onLevelChange(int level) {
420         return mAnimatedVectorState.mVectorDrawable.setLevel(level);
421     }
422 
423     @Override
onLayoutDirectionChanged(@iew.ResolvedLayoutDir int layoutDirection)424     public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
425         return mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
426     }
427 
428     /**
429      * For API 25 and later, AnimatedVectorDrawable runs on RenderThread. Therefore, when the
430      * root alpha is being animated, this getter does not guarantee to return an up-to-date alpha
431      * value.
432      *
433      * @return the containing vector drawable's root alpha value.
434      */
435     @Override
getAlpha()436     public int getAlpha() {
437         return mAnimatedVectorState.mVectorDrawable.getAlpha();
438     }
439 
440     @Override
setAlpha(int alpha)441     public void setAlpha(int alpha) {
442         mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
443     }
444 
445     @Override
setColorFilter(ColorFilter colorFilter)446     public void setColorFilter(ColorFilter colorFilter) {
447         mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
448     }
449 
450     @Override
getColorFilter()451     public ColorFilter getColorFilter() {
452         return mAnimatedVectorState.mVectorDrawable.getColorFilter();
453     }
454 
455     @Override
setTintList(ColorStateList tint)456     public void setTintList(ColorStateList tint) {
457         mAnimatedVectorState.mVectorDrawable.setTintList(tint);
458     }
459 
460     @Override
setHotspot(float x, float y)461     public void setHotspot(float x, float y) {
462         mAnimatedVectorState.mVectorDrawable.setHotspot(x, y);
463     }
464 
465     @Override
setHotspotBounds(int left, int top, int right, int bottom)466     public void setHotspotBounds(int left, int top, int right, int bottom) {
467         mAnimatedVectorState.mVectorDrawable.setHotspotBounds(left, top, right, bottom);
468     }
469 
470     @Override
setTintMode(PorterDuff.Mode tintMode)471     public void setTintMode(PorterDuff.Mode tintMode) {
472         mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
473     }
474 
475     @Override
setVisible(boolean visible, boolean restart)476     public boolean setVisible(boolean visible, boolean restart) {
477         if (mAnimatorSet.isInfinite() && mAnimatorSet.isStarted()) {
478             if (visible) {
479                 // Resume the infinite animation when the drawable becomes visible again.
480                 mAnimatorSet.resume();
481             } else {
482                 // Pause the infinite animation once the drawable is no longer visible.
483                 mAnimatorSet.pause();
484             }
485         }
486         mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
487         return super.setVisible(visible, restart);
488     }
489 
490     @Override
isStateful()491     public boolean isStateful() {
492         return mAnimatedVectorState.mVectorDrawable.isStateful();
493     }
494 
495     @Override
getOpacity()496     public int getOpacity() {
497         return PixelFormat.TRANSLUCENT;
498     }
499 
500     @Override
getIntrinsicWidth()501     public int getIntrinsicWidth() {
502         return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
503     }
504 
505     @Override
getIntrinsicHeight()506     public int getIntrinsicHeight() {
507         return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
508     }
509 
510     @Override
getOutline(@onNull Outline outline)511     public void getOutline(@NonNull Outline outline) {
512         mAnimatedVectorState.mVectorDrawable.getOutline(outline);
513     }
514 
515     /** @hide */
516     @Override
getOpticalInsets()517     public Insets getOpticalInsets() {
518         return mAnimatedVectorState.mVectorDrawable.getOpticalInsets();
519     }
520 
521     @Override
inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)522     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
523             throws XmlPullParserException, IOException {
524         final AnimatedVectorDrawableState state = mAnimatedVectorState;
525 
526         int eventType = parser.getEventType();
527         float pathErrorScale = 1;
528         final int innerDepth = parser.getDepth() + 1;
529 
530         // Parse everything until the end of the animated-vector element.
531         while (eventType != XmlPullParser.END_DOCUMENT
532                 && (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
533             if (eventType == XmlPullParser.START_TAG) {
534                 final String tagName = parser.getName();
535                 if (ANIMATED_VECTOR.equals(tagName)) {
536                     final TypedArray a = obtainAttributes(res, theme, attrs,
537                             R.styleable.AnimatedVectorDrawable);
538                     int drawableRes = a.getResourceId(
539                             R.styleable.AnimatedVectorDrawable_drawable, 0);
540                     if (drawableRes != 0) {
541                         VectorDrawable vectorDrawable = (VectorDrawable) res.getDrawable(
542                                 drawableRes, theme).mutate();
543                         vectorDrawable.setAllowCaching(false);
544                         vectorDrawable.setCallback(mCallback);
545                         pathErrorScale = vectorDrawable.getPixelSize();
546                         if (state.mVectorDrawable != null) {
547                             state.mVectorDrawable.setCallback(null);
548                         }
549                         state.mVectorDrawable = vectorDrawable;
550                     }
551                     a.recycle();
552                 } else if (TARGET.equals(tagName)) {
553                     final TypedArray a = obtainAttributes(res, theme, attrs,
554                             R.styleable.AnimatedVectorDrawableTarget);
555                     final String target = a.getString(
556                             R.styleable.AnimatedVectorDrawableTarget_name);
557                     final int animResId = a.getResourceId(
558                             R.styleable.AnimatedVectorDrawableTarget_animation, 0);
559                     if (animResId != 0) {
560                         if (theme != null) {
561                             // The animator here could be ObjectAnimator or AnimatorSet.
562                             final Animator animator = AnimatorInflater.loadAnimator(
563                                     res, theme, animResId, pathErrorScale);
564                             updateAnimatorProperty(animator, target, state.mVectorDrawable,
565                                     state.mShouldIgnoreInvalidAnim);
566                             state.addTargetAnimator(target, animator);
567                         } else {
568                             // The animation may be theme-dependent. As a
569                             // workaround until Animator has full support for
570                             // applyTheme(), postpone loading the animator
571                             // until we have a theme in applyTheme().
572                             state.addPendingAnimator(animResId, pathErrorScale, target);
573 
574                         }
575                     }
576                     a.recycle();
577                 }
578             }
579 
580             eventType = parser.next();
581         }
582 
583         // If we don't have any pending animations, we don't need to hold a
584         // reference to the resources.
585         mRes = state.mPendingAnims == null ? null : res;
586     }
587 
updateAnimatorProperty(Animator animator, String targetName, VectorDrawable vectorDrawable, boolean ignoreInvalidAnim)588     private static void updateAnimatorProperty(Animator animator, String targetName,
589             VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
590         if (animator instanceof ObjectAnimator) {
591             // Change the property of the Animator from using reflection based on the property
592             // name to a Property object that wraps the setter and getter for modifying that
593             // specific property for a given object. By replacing the reflection with a direct call,
594             // we can largely reduce the time it takes for a animator to modify a VD property.
595             PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues();
596             for (int i = 0; i < holders.length; i++) {
597                 PropertyValuesHolder pvh = holders[i];
598                 String propertyName = pvh.getPropertyName();
599                 Object targetNameObj = vectorDrawable.getTargetByName(targetName);
600                 Property property = null;
601                 if (targetNameObj instanceof VectorDrawable.VObject) {
602                     property = ((VectorDrawable.VObject) targetNameObj).getProperty(propertyName);
603                 } else if (targetNameObj instanceof VectorDrawable.VectorDrawableState) {
604                     property = ((VectorDrawable.VectorDrawableState) targetNameObj)
605                             .getProperty(propertyName);
606                 }
607                 if (property != null) {
608                     if (containsSameValueType(pvh, property)) {
609                         pvh.setProperty(property);
610                     } else if (!ignoreInvalidAnim) {
611                         throw new RuntimeException("Wrong valueType for Property: " + propertyName
612                                 + ".  Expected type: " + property.getType().toString() + ". Actual "
613                                 + "type defined in resources: " + pvh.getValueType().toString());
614 
615                     }
616                 }
617             }
618         } else if (animator instanceof AnimatorSet) {
619             for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) {
620                 updateAnimatorProperty(anim, targetName, vectorDrawable, ignoreInvalidAnim);
621             }
622         }
623     }
624 
containsSameValueType(PropertyValuesHolder holder, Property property)625     private static boolean containsSameValueType(PropertyValuesHolder holder, Property property) {
626         Class type1 = holder.getValueType();
627         Class type2 = property.getType();
628         if (type1 == float.class || type1 == Float.class) {
629             return type2 == float.class || type2 == Float.class;
630         } else if (type1 == int.class || type1 == Integer.class) {
631             return type2 == int.class || type2 == Integer.class;
632         } else {
633             return type1 == type2;
634         }
635     }
636 
637     /**
638      * Force to animate on UI thread.
639      * @hide
640      */
forceAnimationOnUI()641     public void forceAnimationOnUI() {
642         if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
643             VectorDrawableAnimatorRT animator = (VectorDrawableAnimatorRT) mAnimatorSet;
644             if (animator.isRunning()) {
645                 throw new UnsupportedOperationException("Cannot force Animated Vector Drawable to" +
646                         " run on UI thread when the animation has started on RenderThread.");
647             }
648             fallbackOntoUI();
649         }
650     }
651 
fallbackOntoUI()652     private void fallbackOntoUI() {
653         if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
654             VectorDrawableAnimatorRT oldAnim = (VectorDrawableAnimatorRT) mAnimatorSet;
655             mAnimatorSet = new VectorDrawableAnimatorUI(this);
656             if (mAnimatorSetFromXml != null) {
657                 mAnimatorSet.init(mAnimatorSetFromXml);
658             }
659             // Transfer the listener from RT animator to UI animator
660             if (oldAnim.mListener != null) {
661                 mAnimatorSet.setListener(oldAnim.mListener);
662             }
663             oldAnim.transferPendingActions(mAnimatorSet);
664         }
665     }
666 
667     @Override
canApplyTheme()668     public boolean canApplyTheme() {
669         return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme())
670                 || super.canApplyTheme();
671     }
672 
673     @Override
applyTheme(Theme t)674     public void applyTheme(Theme t) {
675         super.applyTheme(t);
676 
677         final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable;
678         if (vectorDrawable != null && vectorDrawable.canApplyTheme()) {
679             vectorDrawable.applyTheme(t);
680         }
681 
682         if (t != null) {
683             mAnimatedVectorState.inflatePendingAnimators(t.getResources(), t);
684         }
685 
686         // If we don't have any pending animations, we don't need to hold a
687         // reference to the resources.
688         if (mAnimatedVectorState.mPendingAnims == null) {
689             mRes = null;
690         }
691     }
692 
693     private static class AnimatedVectorDrawableState extends ConstantState {
694         @Config int mChangingConfigurations;
695         VectorDrawable mVectorDrawable;
696 
697         private final boolean mShouldIgnoreInvalidAnim;
698 
699         /** Animators that require a theme before inflation. */
700         ArrayList<PendingAnimator> mPendingAnims;
701 
702         /** Fully inflated animators awaiting cloning into an AnimatorSet. */
703         ArrayList<Animator> mAnimators;
704 
705         /** Map of animators to their target object names */
706         ArrayMap<Animator, String> mTargetNameMap;
707 
AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, Callback owner, Resources res)708         public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
709                 Callback owner, Resources res) {
710             mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
711             if (copy != null) {
712                 mChangingConfigurations = copy.mChangingConfigurations;
713 
714                 if (copy.mVectorDrawable != null) {
715                     final ConstantState cs = copy.mVectorDrawable.getConstantState();
716                     if (res != null) {
717                         mVectorDrawable = (VectorDrawable) cs.newDrawable(res);
718                     } else {
719                         mVectorDrawable = (VectorDrawable) cs.newDrawable();
720                     }
721                     mVectorDrawable = (VectorDrawable) mVectorDrawable.mutate();
722                     mVectorDrawable.setCallback(owner);
723                     mVectorDrawable.setLayoutDirection(copy.mVectorDrawable.getLayoutDirection());
724                     mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
725                     mVectorDrawable.setAllowCaching(false);
726                 }
727 
728                 if (copy.mAnimators != null) {
729                     mAnimators = new ArrayList<>(copy.mAnimators);
730                 }
731 
732                 if (copy.mTargetNameMap != null) {
733                     mTargetNameMap = new ArrayMap<>(copy.mTargetNameMap);
734                 }
735 
736                 if (copy.mPendingAnims != null) {
737                     mPendingAnims = new ArrayList<>(copy.mPendingAnims);
738                 }
739             } else {
740                 mVectorDrawable = new VectorDrawable();
741             }
742         }
743 
744         @Override
canApplyTheme()745         public boolean canApplyTheme() {
746             return (mVectorDrawable != null && mVectorDrawable.canApplyTheme())
747                     || mPendingAnims != null || super.canApplyTheme();
748         }
749 
750         @Override
newDrawable()751         public Drawable newDrawable() {
752             return new AnimatedVectorDrawable(this, null);
753         }
754 
755         @Override
newDrawable(Resources res)756         public Drawable newDrawable(Resources res) {
757             return new AnimatedVectorDrawable(this, res);
758         }
759 
760         @Override
getChangingConfigurations()761         public @Config int getChangingConfigurations() {
762             return mChangingConfigurations;
763         }
764 
addPendingAnimator(int resId, float pathErrorScale, String target)765         public void addPendingAnimator(int resId, float pathErrorScale, String target) {
766             if (mPendingAnims == null) {
767                 mPendingAnims = new ArrayList<>(1);
768             }
769             mPendingAnims.add(new PendingAnimator(resId, pathErrorScale, target));
770         }
771 
addTargetAnimator(String targetName, Animator animator)772         public void addTargetAnimator(String targetName, Animator animator) {
773             if (mAnimators == null) {
774                 mAnimators = new ArrayList<>(1);
775                 mTargetNameMap = new ArrayMap<>(1);
776             }
777             mAnimators.add(animator);
778             mTargetNameMap.put(animator, targetName);
779 
780             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
781                 Log.v(LOGTAG, "add animator  for target " + targetName + " " + animator);
782             }
783         }
784 
785         /**
786          * Prepares a local set of mutable animators based on the constant
787          * state.
788          * <p>
789          * If there are any pending uninflated animators, attempts to inflate
790          * them immediately against the provided resources object.
791          *
792          * @param animatorSet the animator set to which the animators should
793          *                    be added
794          * @param res the resources against which to inflate any pending
795          *            animators, or {@code null} if not available
796          */
prepareLocalAnimators(@onNull AnimatorSet animatorSet, @Nullable Resources res)797         public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
798                 @Nullable Resources res) {
799             // Check for uninflated animators. We can remove this after we add
800             // support for Animator.applyTheme(). See comments in inflate().
801             if (mPendingAnims != null) {
802                 // Attempt to load animators without applying a theme.
803                 if (res != null) {
804                     inflatePendingAnimators(res, null);
805                 } else {
806                     Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable"
807                             + " must be created using a Resources object or applyTheme() must be"
808                             + " called with a non-null Theme object.");
809                 }
810 
811                 mPendingAnims = null;
812             }
813 
814             // Perform a deep copy of the constant state's animators.
815             final int count = mAnimators == null ? 0 : mAnimators.size();
816             if (count > 0) {
817                 final Animator firstAnim = prepareLocalAnimator(0);
818                 final AnimatorSet.Builder builder = animatorSet.play(firstAnim);
819                 for (int i = 1; i < count; ++i) {
820                     final Animator nextAnim = prepareLocalAnimator(i);
821                     builder.with(nextAnim);
822                 }
823             }
824         }
825 
826         /**
827          * Prepares a local animator for the given index within the constant
828          * state's list of animators.
829          *
830          * @param index the index of the animator within the constant state
831          */
prepareLocalAnimator(int index)832         private Animator prepareLocalAnimator(int index) {
833             final Animator animator = mAnimators.get(index);
834             final Animator localAnimator = animator.clone();
835             final String targetName = mTargetNameMap.get(animator);
836             final Object target = mVectorDrawable.getTargetByName(targetName);
837             if (!mShouldIgnoreInvalidAnim) {
838                 if (target == null) {
839                     throw new IllegalStateException("Target with the name \"" + targetName
840                             + "\" cannot be found in the VectorDrawable to be animated.");
841                 } else if (!(target instanceof VectorDrawable.VectorDrawableState)
842                         && !(target instanceof VectorDrawable.VObject)) {
843                     throw new UnsupportedOperationException("Target should be either VGroup, VPath,"
844                             + " or ConstantState, " + target.getClass() + " is not supported");
845                 }
846             }
847             localAnimator.setTarget(target);
848             return localAnimator;
849         }
850 
851         /**
852          * Inflates pending animators, if any, against a theme. Clears the list of
853          * pending animators.
854          *
855          * @param t the theme against which to inflate the animators
856          */
inflatePendingAnimators(@onNull Resources res, @Nullable Theme t)857         public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) {
858             final ArrayList<PendingAnimator> pendingAnims = mPendingAnims;
859             if (pendingAnims != null) {
860                 mPendingAnims = null;
861 
862                 for (int i = 0, count = pendingAnims.size(); i < count; i++) {
863                     final PendingAnimator pendingAnimator = pendingAnims.get(i);
864                     final Animator animator = pendingAnimator.newInstance(res, t);
865                     updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
866                             mShouldIgnoreInvalidAnim);
867                     addTargetAnimator(pendingAnimator.target, animator);
868                 }
869             }
870         }
871 
872         /**
873          * Basically a constant state for Animators until we actually implement
874          * constant states for Animators.
875          */
876         private static class PendingAnimator {
877             public final int animResId;
878             public final float pathErrorScale;
879             public final String target;
880 
PendingAnimator(int animResId, float pathErrorScale, String target)881             public PendingAnimator(int animResId, float pathErrorScale, String target) {
882                 this.animResId = animResId;
883                 this.pathErrorScale = pathErrorScale;
884                 this.target = target;
885             }
886 
newInstance(Resources res, Theme theme)887             public Animator newInstance(Resources res, Theme theme) {
888                 return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
889             }
890         }
891     }
892 
893     @Override
isRunning()894     public boolean isRunning() {
895         return mAnimatorSet.isRunning();
896     }
897 
898     /**
899      * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
900      */
reset()901     public void reset() {
902         ensureAnimatorSet();
903         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
904             Log.w(LOGTAG, "calling reset on AVD: " +
905                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
906                     getConstantState()).mVectorDrawable.getConstantState()).mRootName
907                     + ", at: " + this);
908         }
909         mAnimatorSet.reset();
910     }
911 
912     @Override
start()913     public void start() {
914         ensureAnimatorSet();
915         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
916             Log.w(LOGTAG, "calling start on AVD: " +
917                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
918                     getConstantState()).mVectorDrawable.getConstantState()).mRootName
919                     + ", at: " + this);
920         }
921         mAnimatorSet.start();
922     }
923 
924     @NonNull
ensureAnimatorSet()925     private void ensureAnimatorSet() {
926         if (mAnimatorSetFromXml == null) {
927             // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
928             // with a list of LocalAnimators.
929             mAnimatorSetFromXml = new AnimatorSet();
930             mAnimatedVectorState.prepareLocalAnimators(mAnimatorSetFromXml, mRes);
931             mAnimatorSet.init(mAnimatorSetFromXml);
932             mRes = null;
933         }
934     }
935 
936     @Override
stop()937     public void stop() {
938         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
939             Log.w(LOGTAG, "calling stop on AVD: " +
940                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
941                             getConstantState()).mVectorDrawable.getConstantState())
942                             .mRootName + ", at: " + this);
943         }
944         mAnimatorSet.end();
945     }
946 
947     /**
948      * Reverses ongoing animations or starts pending animations in reverse.
949      * <p>
950      * NOTE: Only works if all animations support reverse. Otherwise, this will
951      * do nothing.
952      * @hide
953      */
reverse()954     public void reverse() {
955         ensureAnimatorSet();
956 
957         // Only reverse when all the animators can be reversed.
958         if (!canReverse()) {
959             Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
960             return;
961         }
962 
963         mAnimatorSet.reverse();
964     }
965 
966     /**
967      * @hide
968      */
canReverse()969     public boolean canReverse() {
970         return mAnimatorSet.canReverse();
971     }
972 
973     private final Callback mCallback = new Callback() {
974         @Override
975         public void invalidateDrawable(@NonNull Drawable who) {
976             invalidateSelf();
977         }
978 
979         @Override
980         public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
981             scheduleSelf(what, when);
982         }
983 
984         @Override
985         public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
986             unscheduleSelf(what);
987         }
988     };
989 
990     @Override
registerAnimationCallback(@onNull AnimationCallback callback)991     public void registerAnimationCallback(@NonNull AnimationCallback callback) {
992         if (callback == null) {
993             return;
994         }
995 
996         // Add listener accordingly.
997         if (mAnimationCallbacks == null) {
998             mAnimationCallbacks = new ArrayList<>();
999         }
1000 
1001         mAnimationCallbacks.add(callback);
1002 
1003         if (mAnimatorListener == null) {
1004             // Create a animator listener and trigger the callback events when listener is
1005             // triggered.
1006             mAnimatorListener = new AnimatorListenerAdapter() {
1007                 @Override
1008                 public void onAnimationStart(Animator animation) {
1009                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1010                     int size = tmpCallbacks.size();
1011                     for (int i = 0; i < size; i ++) {
1012                         tmpCallbacks.get(i).onAnimationStart(AnimatedVectorDrawable.this);
1013                     }
1014                 }
1015 
1016                 @Override
1017                 public void onAnimationEnd(Animator animation) {
1018                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1019                     int size = tmpCallbacks.size();
1020                     for (int i = 0; i < size; i ++) {
1021                         tmpCallbacks.get(i).onAnimationEnd(AnimatedVectorDrawable.this);
1022                     }
1023                 }
1024             };
1025         }
1026         mAnimatorSet.setListener(mAnimatorListener);
1027     }
1028 
1029     // A helper function to clean up the animator listener in the mAnimatorSet.
removeAnimatorSetListener()1030     private void removeAnimatorSetListener() {
1031         if (mAnimatorListener != null) {
1032             mAnimatorSet.removeListener(mAnimatorListener);
1033             mAnimatorListener = null;
1034         }
1035     }
1036 
1037     @Override
unregisterAnimationCallback(@onNull AnimationCallback callback)1038     public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
1039         if (mAnimationCallbacks == null || callback == null) {
1040             // Nothing to be removed.
1041             return false;
1042         }
1043         boolean removed = mAnimationCallbacks.remove(callback);
1044 
1045         //  When the last call back unregistered, remove the listener accordingly.
1046         if (mAnimationCallbacks.size() == 0) {
1047             removeAnimatorSetListener();
1048         }
1049         return removed;
1050     }
1051 
1052     @Override
clearAnimationCallbacks()1053     public void clearAnimationCallbacks() {
1054         removeAnimatorSetListener();
1055         if (mAnimationCallbacks == null) {
1056             return;
1057         }
1058 
1059         mAnimationCallbacks.clear();
1060     }
1061 
1062     private interface VectorDrawableAnimator {
init(@onNull AnimatorSet set)1063         void init(@NonNull AnimatorSet set);
start()1064         void start();
end()1065         void end();
reset()1066         void reset();
reverse()1067         void reverse();
canReverse()1068         boolean canReverse();
setListener(AnimatorListener listener)1069         void setListener(AnimatorListener listener);
removeListener(AnimatorListener listener)1070         void removeListener(AnimatorListener listener);
onDraw(Canvas canvas)1071         void onDraw(Canvas canvas);
isStarted()1072         boolean isStarted();
isRunning()1073         boolean isRunning();
isInfinite()1074         boolean isInfinite();
pause()1075         void pause();
resume()1076         void resume();
1077     }
1078 
1079     private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
1080         // mSet is only initialized in init(). So we need to check whether it is null before any
1081         // operation.
1082         private AnimatorSet mSet = null;
1083         private final Drawable mDrawable;
1084         // Caching the listener in the case when listener operation is called before the mSet is
1085         // setup by init().
1086         private ArrayList<AnimatorListener> mListenerArray = null;
1087         private boolean mIsInfinite = false;
1088 
VectorDrawableAnimatorUI(@onNull AnimatedVectorDrawable drawable)1089         VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
1090             mDrawable = drawable;
1091         }
1092 
1093         @Override
init(@onNull AnimatorSet set)1094         public void init(@NonNull AnimatorSet set) {
1095             if (mSet != null) {
1096                 // Already initialized
1097                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1098                         "re-initialized");
1099             }
1100             // Keep a deep copy of the set, such that set can be still be constantly representing
1101             // the static content from XML file.
1102             mSet = set.clone();
1103             mIsInfinite = mSet.getTotalDuration() == Animator.DURATION_INFINITE;
1104 
1105             // If there are listeners added before calling init(), now they should be setup.
1106             if (mListenerArray != null && !mListenerArray.isEmpty()) {
1107                 for (int i = 0; i < mListenerArray.size(); i++) {
1108                     mSet.addListener(mListenerArray.get(i));
1109                 }
1110                 mListenerArray.clear();
1111                 mListenerArray = null;
1112             }
1113         }
1114 
1115         // Although start(), reset() and reverse() should call init() already, it is better to
1116         // protect these functions from NPE in any situation.
1117         @Override
start()1118         public void start() {
1119             if (mSet == null || mSet.isStarted()) {
1120                 return;
1121             }
1122             mSet.start();
1123             invalidateOwningView();
1124         }
1125 
1126         @Override
end()1127         public void end() {
1128             if (mSet == null) {
1129                 return;
1130             }
1131             mSet.end();
1132         }
1133 
1134         @Override
reset()1135         public void reset() {
1136             if (mSet == null) {
1137                 return;
1138             }
1139             start();
1140             mSet.cancel();
1141         }
1142 
1143         @Override
reverse()1144         public void reverse() {
1145             if (mSet == null) {
1146                 return;
1147             }
1148             mSet.reverse();
1149             invalidateOwningView();
1150         }
1151 
1152         @Override
canReverse()1153         public boolean canReverse() {
1154             return mSet != null && mSet.canReverse();
1155         }
1156 
1157         @Override
setListener(AnimatorListener listener)1158         public void setListener(AnimatorListener listener) {
1159             if (mSet == null) {
1160                 if (mListenerArray == null) {
1161                     mListenerArray = new ArrayList<AnimatorListener>();
1162                 }
1163                 mListenerArray.add(listener);
1164             } else {
1165                 mSet.addListener(listener);
1166             }
1167         }
1168 
1169         @Override
removeListener(AnimatorListener listener)1170         public void removeListener(AnimatorListener listener) {
1171             if (mSet == null) {
1172                 if (mListenerArray == null) {
1173                     return;
1174                 }
1175                 mListenerArray.remove(listener);
1176             } else {
1177                 mSet.removeListener(listener);
1178             }
1179         }
1180 
1181         @Override
onDraw(Canvas canvas)1182         public void onDraw(Canvas canvas) {
1183             if (mSet != null && mSet.isStarted()) {
1184                 invalidateOwningView();
1185             }
1186         }
1187 
1188         @Override
isStarted()1189         public boolean isStarted() {
1190             return mSet != null && mSet.isStarted();
1191         }
1192 
1193         @Override
isRunning()1194         public boolean isRunning() {
1195             return mSet != null && mSet.isRunning();
1196         }
1197 
1198         @Override
isInfinite()1199         public boolean isInfinite() {
1200             return mIsInfinite;
1201         }
1202 
1203         @Override
pause()1204         public void pause() {
1205             if (mSet == null) {
1206                 return;
1207             }
1208             mSet.pause();
1209         }
1210 
1211         @Override
resume()1212         public void resume() {
1213             if (mSet == null) {
1214                 return;
1215             }
1216             mSet.resume();
1217         }
1218 
invalidateOwningView()1219         private void invalidateOwningView() {
1220             mDrawable.invalidateSelf();
1221         }
1222     }
1223 
1224     /**
1225      * @hide
1226      */
1227     public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator {
1228         private static final int START_ANIMATION = 1;
1229         private static final int REVERSE_ANIMATION = 2;
1230         private static final int RESET_ANIMATION = 3;
1231         private static final int END_ANIMATION = 4;
1232 
1233         // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
1234         private static final int MAX_SAMPLE_POINTS = 300;
1235         private AnimatorListener mListener = null;
1236         private final LongArray mStartDelays = new LongArray();
1237         private PropertyValuesHolder.PropertyValues mTmpValues =
1238                 new PropertyValuesHolder.PropertyValues();
1239         private long mSetPtr = 0;
1240         private boolean mContainsSequentialAnimators = false;
1241         private boolean mStarted = false;
1242         private boolean mInitialized = false;
1243         private boolean mIsReversible = false;
1244         private boolean mIsInfinite = false;
1245         // TODO: Consider using NativeAllocationRegistery to track native allocation
1246         private final VirtualRefBasePtr mSetRefBasePtr;
1247         private WeakReference<RenderNode> mLastSeenTarget = null;
1248         private int mLastListenerId = 0;
1249         private final IntArray mPendingAnimationActions = new IntArray();
1250         private final AnimatedVectorDrawable mDrawable;
1251 
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable)1252         VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
1253             mDrawable = drawable;
1254             mSetPtr = nCreateAnimatorSet();
1255             // Increment ref count on native AnimatorSet, so it doesn't get released before Java
1256             // side is done using it.
1257             mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
1258         }
1259 
1260         @Override
init(@onNull AnimatorSet set)1261         public void init(@NonNull AnimatorSet set) {
1262             if (mInitialized) {
1263                 // Already initialized
1264                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1265                         "re-initialized");
1266             }
1267             parseAnimatorSet(set, 0);
1268             long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
1269                     .getNativeTree();
1270             nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
1271             mInitialized = true;
1272             mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
1273 
1274             // Check reversible.
1275             mIsReversible = true;
1276             if (mContainsSequentialAnimators) {
1277                 mIsReversible = false;
1278             } else {
1279                 // Check if there's any start delay set on child
1280                 for (int i = 0; i < mStartDelays.size(); i++) {
1281                     if (mStartDelays.get(i) > 0) {
1282                         mIsReversible = false;
1283                         return;
1284                     }
1285                 }
1286             }
1287         }
1288 
parseAnimatorSet(AnimatorSet set, long startTime)1289         private void parseAnimatorSet(AnimatorSet set, long startTime) {
1290             ArrayList<Animator> animators = set.getChildAnimations();
1291 
1292             boolean playTogether = set.shouldPlayTogether();
1293             // Convert AnimatorSet to VectorDrawableAnimatorRT
1294             for (int i = 0; i < animators.size(); i++) {
1295                 Animator animator = animators.get(i);
1296                 // Here we only support ObjectAnimator
1297                 if (animator instanceof AnimatorSet) {
1298                     parseAnimatorSet((AnimatorSet) animator, startTime);
1299                 } else if (animator instanceof ObjectAnimator) {
1300                     createRTAnimator((ObjectAnimator) animator, startTime);
1301                 } // ignore ValueAnimators and others because they don't directly modify VD
1302                   // therefore will be useless to AVD.
1303 
1304                 if (!playTogether) {
1305                     // Assume not play together means play sequentially
1306                     startTime += animator.getTotalDuration();
1307                     mContainsSequentialAnimators = true;
1308                 }
1309             }
1310         }
1311 
1312         // TODO: This method reads animation data from already parsed Animators. We need to move
1313         // this step further up the chain in the parser to avoid the detour.
createRTAnimator(ObjectAnimator animator, long startTime)1314         private void createRTAnimator(ObjectAnimator animator, long startTime) {
1315             PropertyValuesHolder[] values = animator.getValues();
1316             Object target = animator.getTarget();
1317             if (target instanceof VectorDrawable.VGroup) {
1318                 createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
1319                         startTime);
1320             } else if (target instanceof VectorDrawable.VPath) {
1321                 for (int i = 0; i < values.length; i++) {
1322                     values[i].getPropertyValues(mTmpValues);
1323                     if (mTmpValues.endValue instanceof PathParser.PathData &&
1324                             mTmpValues.propertyName.equals("pathData")) {
1325                         createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
1326                                 startTime);
1327                     }  else if (target instanceof VectorDrawable.VFullPath) {
1328                         createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
1329                                 startTime);
1330                     } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1331                         throw new IllegalArgumentException("ClipPath only supports PathData " +
1332                                 "property");
1333                     }
1334                 }
1335             } else if (target instanceof VectorDrawable.VectorDrawableState) {
1336                 createRTAnimatorForRootGroup(values, animator,
1337                         (VectorDrawable.VectorDrawableState) target, startTime);
1338             }
1339         }
1340 
createRTAnimatorForGroup(PropertyValuesHolder[] values, ObjectAnimator animator, VectorDrawable.VGroup target, long startTime)1341         private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
1342                 ObjectAnimator animator, VectorDrawable.VGroup target,
1343                 long startTime) {
1344 
1345             long nativePtr = target.getNativePtr();
1346             int propertyId;
1347             for (int i = 0; i < values.length; i++) {
1348                 // TODO: We need to support the rare case in AVD where no start value is provided
1349                 values[i].getPropertyValues(mTmpValues);
1350                 propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
1351                 if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
1352                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1353                         Log.e(LOGTAG, "Unsupported type: " +
1354                                 mTmpValues.type + ". Only float value is supported for Groups.");
1355                     }
1356                     continue;
1357                 }
1358                 if (propertyId < 0) {
1359                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1360                         Log.e(LOGTAG, "Unsupported property: " +
1361                                 mTmpValues.propertyName + " for Vector Drawable Group");
1362                     }
1363                     continue;
1364                 }
1365                 long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
1366                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1367                 if (mTmpValues.dataSource != null) {
1368                     float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1369                             animator.getDuration());
1370                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1371                 }
1372                 createNativeChildAnimator(propertyPtr, startTime, animator);
1373             }
1374         }
createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target, long startTime)1375         private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
1376                 long startTime) {
1377 
1378             long nativePtr = target.getNativePtr();
1379             long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
1380                     .getNativePtr();
1381             long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
1382                     .getNativePtr();
1383             long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
1384                     endPathDataPtr);
1385             createNativeChildAnimator(propertyPtr, startTime, animator);
1386         }
1387 
createRTAnimatorForFullPath(ObjectAnimator animator, VectorDrawable.VFullPath target, long startTime)1388         private void createRTAnimatorForFullPath(ObjectAnimator animator,
1389                 VectorDrawable.VFullPath target, long startTime) {
1390 
1391             int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
1392             long propertyPtr;
1393             long nativePtr = target.getNativePtr();
1394             if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
1395                 if (propertyId < 0) {
1396                     if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1397                         return;
1398                     } else {
1399                         throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
1400                                 + " is not supported for FullPath");
1401                     }
1402                 }
1403                 propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
1404                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1405                 if (mTmpValues.dataSource != null) {
1406                     // Pass keyframe data to native, if any.
1407                     float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1408                             animator.getDuration());
1409                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1410                 }
1411 
1412             } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
1413                 propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
1414                         (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
1415                 if (mTmpValues.dataSource != null) {
1416                     // Pass keyframe data to native, if any.
1417                     int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
1418                             animator.getDuration());
1419                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1420                 }
1421             } else {
1422                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1423                     return;
1424                 } else {
1425                     throw new UnsupportedOperationException("Unsupported type: " +
1426                             mTmpValues.type + ". Only float, int or PathData value is " +
1427                             "supported for Paths.");
1428                 }
1429             }
1430             createNativeChildAnimator(propertyPtr, startTime, animator);
1431         }
1432 
createRTAnimatorForRootGroup(PropertyValuesHolder[] values, ObjectAnimator animator, VectorDrawable.VectorDrawableState target, long startTime)1433         private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
1434                 ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
1435                 long startTime) {
1436             long nativePtr = target.getNativeRenderer();
1437             if (!animator.getPropertyName().equals("alpha")) {
1438                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1439                     return;
1440                 } else {
1441                     throw new UnsupportedOperationException("Only alpha is supported for root "
1442                             + "group");
1443                 }
1444             }
1445             Float startValue = null;
1446             Float endValue = null;
1447             for (int i = 0; i < values.length; i++) {
1448                 values[i].getPropertyValues(mTmpValues);
1449                 if (mTmpValues.propertyName.equals("alpha")) {
1450                     startValue = (Float) mTmpValues.startValue;
1451                     endValue = (Float) mTmpValues.endValue;
1452                     break;
1453                 }
1454             }
1455             if (startValue == null && endValue == null) {
1456                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1457                     return;
1458                 } else {
1459                     throw new UnsupportedOperationException("No alpha values are specified");
1460                 }
1461             }
1462             long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
1463             if (mTmpValues.dataSource != null) {
1464                 // Pass keyframe data to native, if any.
1465                 float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1466                         animator.getDuration());
1467                 nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1468             }
1469             createNativeChildAnimator(propertyPtr, startTime, animator);
1470         }
1471 
1472         /**
1473          * Calculate the amount of frames an animation will run based on duration.
1474          */
getFrameCount(long duration)1475         private static int getFrameCount(long duration) {
1476             long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
1477             int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
1478             int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
1479             // We need 2 frames of data minimum.
1480             numAnimFrames = Math.max(2, numAnimFrames);
1481             if (numAnimFrames > MAX_SAMPLE_POINTS) {
1482                 Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
1483                         duration + ", the animation will subsample the keyframe or path data.");
1484                 numAnimFrames = MAX_SAMPLE_POINTS;
1485             }
1486             return numAnimFrames;
1487         }
1488 
1489         // These are the data points that define the value of the animating properties.
1490         // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
1491         // a point on the path corresponds to the values of translateX and translateY.
1492         // TODO: (Optimization) We should pass the path down in native and chop it into segments
1493         // in native.
createFloatDataPoints( PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration)1494         private static float[] createFloatDataPoints(
1495                 PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1496             int numAnimFrames = getFrameCount(duration);
1497             float values[] = new float[numAnimFrames];
1498             float lastFrame = numAnimFrames - 1;
1499             for (int i = 0; i < numAnimFrames; i++) {
1500                 float fraction = i / lastFrame;
1501                 values[i] = (Float) dataSource.getValueAtFraction(fraction);
1502             }
1503             return values;
1504         }
1505 
createIntDataPoints( PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration)1506         private static int[] createIntDataPoints(
1507                 PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1508             int numAnimFrames = getFrameCount(duration);
1509             int values[] = new int[numAnimFrames];
1510             float lastFrame = numAnimFrames - 1;
1511             for (int i = 0; i < numAnimFrames; i++) {
1512                 float fraction = i / lastFrame;
1513                 values[i] = (Integer) dataSource.getValueAtFraction(fraction);
1514             }
1515             return values;
1516         }
1517 
createNativeChildAnimator(long propertyPtr, long extraDelay, ObjectAnimator animator)1518         private void createNativeChildAnimator(long propertyPtr, long extraDelay,
1519                                                ObjectAnimator animator) {
1520             long duration = animator.getDuration();
1521             int repeatCount = animator.getRepeatCount();
1522             long startDelay = extraDelay + animator.getStartDelay();
1523             TimeInterpolator interpolator = animator.getInterpolator();
1524             long nativeInterpolator =
1525                     RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
1526 
1527             startDelay *= ValueAnimator.getDurationScale();
1528             duration *= ValueAnimator.getDurationScale();
1529 
1530             mStartDelays.add(startDelay);
1531             nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
1532                     repeatCount, animator.getRepeatMode());
1533         }
1534 
1535         /**
1536          * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
1537          * in the last draw call), so that when animator set needs to start, we can add the animator
1538          * to the last seen RenderNode target and start right away.
1539          */
recordLastSeenTarget(DisplayListCanvas canvas)1540         protected void recordLastSeenTarget(DisplayListCanvas canvas) {
1541             final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
1542             mLastSeenTarget = new WeakReference<RenderNode>(node);
1543             // Add the animator to the list of animators on every draw
1544             if (mInitialized || mPendingAnimationActions.size() > 0) {
1545                 if (useTarget(node)) {
1546                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1547                         Log.d(LOGTAG, "Target is set in the next frame");
1548                     }
1549                     for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1550                         handlePendingAction(mPendingAnimationActions.get(i));
1551                     }
1552                     mPendingAnimationActions.clear();
1553                 }
1554             }
1555         }
1556 
handlePendingAction(int pendingAnimationAction)1557         private void handlePendingAction(int pendingAnimationAction) {
1558             if (pendingAnimationAction == START_ANIMATION) {
1559                 startAnimation();
1560             } else if (pendingAnimationAction == REVERSE_ANIMATION) {
1561                 reverseAnimation();
1562             } else if (pendingAnimationAction == RESET_ANIMATION) {
1563                 resetAnimation();
1564             } else if (pendingAnimationAction == END_ANIMATION) {
1565                 endAnimation();
1566             } else {
1567                 throw new UnsupportedOperationException("Animation action " +
1568                         pendingAnimationAction + "is not supported");
1569             }
1570         }
1571 
useLastSeenTarget()1572         private boolean useLastSeenTarget() {
1573             if (mLastSeenTarget != null) {
1574                 final RenderNode target = mLastSeenTarget.get();
1575                 return useTarget(target);
1576             }
1577             return false;
1578         }
1579 
useTarget(RenderNode target)1580         private boolean useTarget(RenderNode target) {
1581             if (target != null && target.isAttached()) {
1582                 target.registerVectorDrawableAnimator(this);
1583                 return true;
1584             }
1585             return false;
1586         }
1587 
invalidateOwningView()1588         private void invalidateOwningView() {
1589             mDrawable.invalidateSelf();
1590         }
1591 
addPendingAction(int pendingAnimationAction)1592         private void addPendingAction(int pendingAnimationAction) {
1593             invalidateOwningView();
1594             mPendingAnimationActions.add(pendingAnimationAction);
1595         }
1596 
1597         @Override
start()1598         public void start() {
1599             if (!mInitialized) {
1600                 return;
1601             }
1602 
1603             if (useLastSeenTarget()) {
1604                 if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1605                     Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
1606                 }
1607                 startAnimation();
1608             } else {
1609                 addPendingAction(START_ANIMATION);
1610             }
1611         }
1612 
1613         @Override
end()1614         public void end() {
1615             if (!mInitialized) {
1616                 return;
1617             }
1618 
1619             if (useLastSeenTarget()) {
1620                 endAnimation();
1621             } else {
1622                 addPendingAction(END_ANIMATION);
1623             }
1624         }
1625 
1626         @Override
reset()1627         public void reset() {
1628             if (!mInitialized) {
1629                 return;
1630             }
1631 
1632             if (useLastSeenTarget()) {
1633                 resetAnimation();
1634             } else {
1635                 addPendingAction(RESET_ANIMATION);
1636             }
1637         }
1638 
1639         // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
1640         // animators or when the animator set has a start delay
1641         @Override
reverse()1642         public void reverse() {
1643             if (!mIsReversible || !mInitialized) {
1644                 return;
1645             }
1646             if (useLastSeenTarget()) {
1647                 if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1648                     Log.d(LOGTAG, "Target is set. Reversing VDAnimatorSet from java");
1649                 }
1650                 reverseAnimation();
1651             } else {
1652                 addPendingAction(REVERSE_ANIMATION);
1653             }
1654         }
1655 
1656         // This should only be called after animator has been added to the RenderNode target.
startAnimation()1657         private void startAnimation() {
1658             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1659                 Log.w(LOGTAG, "starting animation on VD: " +
1660                         ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1661                                 mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1662                                 .mRootName);
1663             }
1664             mStarted = true;
1665             nStart(mSetPtr, this, ++mLastListenerId);
1666             invalidateOwningView();
1667             if (mListener != null) {
1668                 mListener.onAnimationStart(null);
1669             }
1670         }
1671 
1672         // This should only be called after animator has been added to the RenderNode target.
endAnimation()1673         private void endAnimation() {
1674             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1675                 Log.w(LOGTAG, "ending animation on VD: " +
1676                         ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1677                                 mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1678                                 .mRootName);
1679             }
1680             nEnd(mSetPtr);
1681             invalidateOwningView();
1682         }
1683 
1684         // This should only be called after animator has been added to the RenderNode target.
resetAnimation()1685         private void resetAnimation() {
1686             nReset(mSetPtr);
1687             invalidateOwningView();
1688         }
1689 
1690         // This should only be called after animator has been added to the RenderNode target.
reverseAnimation()1691         private void reverseAnimation() {
1692             mStarted = true;
1693             nReverse(mSetPtr, this, ++mLastListenerId);
1694             invalidateOwningView();
1695             if (mListener != null) {
1696                 mListener.onAnimationStart(null);
1697             }
1698         }
1699 
getAnimatorNativePtr()1700         public long getAnimatorNativePtr() {
1701             return mSetPtr;
1702         }
1703 
1704         @Override
canReverse()1705         public boolean canReverse() {
1706             return mIsReversible;
1707         }
1708 
1709         @Override
isStarted()1710         public boolean isStarted() {
1711             return mStarted;
1712         }
1713 
1714         @Override
isRunning()1715         public boolean isRunning() {
1716             if (!mInitialized) {
1717                 return false;
1718             }
1719             return mStarted;
1720         }
1721 
1722         @Override
setListener(AnimatorListener listener)1723         public void setListener(AnimatorListener listener) {
1724             mListener = listener;
1725         }
1726 
1727         @Override
removeListener(AnimatorListener listener)1728         public void removeListener(AnimatorListener listener) {
1729             mListener = null;
1730         }
1731 
1732         @Override
onDraw(Canvas canvas)1733         public void onDraw(Canvas canvas) {
1734             if (canvas.isHardwareAccelerated()) {
1735                 recordLastSeenTarget((DisplayListCanvas) canvas);
1736             }
1737         }
1738 
1739         @Override
isInfinite()1740         public boolean isInfinite() {
1741             return mIsInfinite;
1742         }
1743 
1744         @Override
pause()1745         public void pause() {
1746             // TODO: Implement pause for Animator On RT.
1747         }
1748 
1749         @Override
resume()1750         public void resume() {
1751             // TODO: Implement resume for Animator On RT.
1752         }
1753 
onAnimationEnd(int listenerId)1754         private void onAnimationEnd(int listenerId) {
1755             if (listenerId != mLastListenerId) {
1756                 return;
1757             }
1758             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1759                 Log.d(LOGTAG, "on finished called from native");
1760             }
1761             mStarted = false;
1762             // Invalidate in the end of the animation to make sure the data in
1763             // RT thread is synced back to UI thread.
1764             invalidateOwningView();
1765             if (mListener != null) {
1766                 mListener.onAnimationEnd(null);
1767             }
1768         }
1769 
1770         // onFinished: should be called from native
callOnFinished(VectorDrawableAnimatorRT set, int id)1771         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
1772             set.onAnimationEnd(id);
1773         }
1774 
transferPendingActions(VectorDrawableAnimator animatorSet)1775         private void transferPendingActions(VectorDrawableAnimator animatorSet) {
1776             for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1777                 int pendingAction = mPendingAnimationActions.get(i);
1778                 if (pendingAction == START_ANIMATION) {
1779                     animatorSet.start();
1780                 } else if (pendingAction == END_ANIMATION) {
1781                     animatorSet.end();
1782                 } else if (pendingAction == REVERSE_ANIMATION) {
1783                     animatorSet.reverse();
1784                 } else if (pendingAction == RESET_ANIMATION) {
1785                     animatorSet.reset();
1786                 } else {
1787                     throw new UnsupportedOperationException("Animation action " +
1788                             pendingAction + "is not supported");
1789                 }
1790             }
1791             mPendingAnimationActions.clear();
1792         }
1793     }
1794 
nCreateAnimatorSet()1795     private static native long nCreateAnimatorSet();
nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr)1796     private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
nAddAnimator(long setPtr, long propertyValuesHolder, long nativeInterpolator, long startDelay, long duration, int repeatCount, int repeatMode)1797     private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
1798             long nativeInterpolator, long startDelay, long duration, int repeatCount,
1799             int repeatMode);
nSetPropertyHolderData(long nativePtr, float[] data, int length)1800     private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
nSetPropertyHolderData(long nativePtr, int[] data, int length)1801     private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id)1802     private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id)1803     private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
1804 
1805     // ------------- @FastNative -------------------
1806 
1807     @FastNative
nCreateGroupPropertyHolder(long nativePtr, int propertyId, float startValue, float endValue)1808     private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
1809             float startValue, float endValue);
1810     @FastNative
nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr, long endValuePtr)1811     private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
1812             long endValuePtr);
1813     @FastNative
nCreatePathColorPropertyHolder(long nativePtr, int propertyId, int startValue, int endValue)1814     private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
1815             int startValue, int endValue);
1816     @FastNative
nCreatePathPropertyHolder(long nativePtr, int propertyId, float startValue, float endValue)1817     private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
1818             float startValue, float endValue);
1819     @FastNative
nCreateRootAlphaPropertyHolder(long nativePtr, float startValue, float endValue)1820     private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
1821             float endValue);
1822     @FastNative
nEnd(long animatorSetPtr)1823     private static native void nEnd(long animatorSetPtr);
1824     @FastNative
nReset(long animatorSetPtr)1825     private static native void nReset(long animatorSetPtr);
1826 }
1827