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