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