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     private static class AnimatedVectorDrawableState extends ConstantState {
694         @Config int mChangingConfigurations;
695         VectorDrawable mVectorDrawable;
696 
697         private final boolean mShouldIgnoreInvalidAnim;
698 
699         /** Animators that require a theme before inflation. */
700         ArrayList<PendingAnimator> mPendingAnims;
701 
702         /** Fully inflated animators awaiting cloning into an AnimatorSet. */
703         ArrayList<Animator> mAnimators;
704 
705         /** Map of animators to their target object names */
706         ArrayMap<Animator, String> mTargetNameMap;
707 
AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, Callback owner, Resources res)708         public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
709                 Callback owner, Resources res) {
710             mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
711             if (copy != null) {
712                 mChangingConfigurations = copy.mChangingConfigurations;
713 
714                 if (copy.mVectorDrawable != null) {
715                     final ConstantState cs = copy.mVectorDrawable.getConstantState();
716                     if (res != null) {
717                         mVectorDrawable = (VectorDrawable) cs.newDrawable(res);
718                     } else {
719                         mVectorDrawable = (VectorDrawable) cs.newDrawable();
720                     }
721                     mVectorDrawable = (VectorDrawable) mVectorDrawable.mutate();
722                     mVectorDrawable.setCallback(owner);
723                     mVectorDrawable.setLayoutDirection(copy.mVectorDrawable.getLayoutDirection());
724                     mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
725                     mVectorDrawable.setAllowCaching(false);
726                 }
727 
728                 if (copy.mAnimators != null) {
729                     mAnimators = new ArrayList<>(copy.mAnimators);
730                 }
731 
732                 if (copy.mTargetNameMap != null) {
733                     mTargetNameMap = new ArrayMap<>(copy.mTargetNameMap);
734                 }
735 
736                 if (copy.mPendingAnims != null) {
737                     mPendingAnims = new ArrayList<>(copy.mPendingAnims);
738                 }
739             } else {
740                 mVectorDrawable = new VectorDrawable();
741             }
742         }
743 
744         @Override
canApplyTheme()745         public boolean canApplyTheme() {
746             return (mVectorDrawable != null && mVectorDrawable.canApplyTheme())
747                     || mPendingAnims != null || super.canApplyTheme();
748         }
749 
750         @Override
newDrawable()751         public Drawable newDrawable() {
752             return new AnimatedVectorDrawable(this, null);
753         }
754 
755         @Override
newDrawable(Resources res)756         public Drawable newDrawable(Resources res) {
757             return new AnimatedVectorDrawable(this, res);
758         }
759 
760         @Override
getChangingConfigurations()761         public @Config int getChangingConfigurations() {
762             return mChangingConfigurations;
763         }
764 
addPendingAnimator(int resId, float pathErrorScale, String target)765         public void addPendingAnimator(int resId, float pathErrorScale, String target) {
766             if (mPendingAnims == null) {
767                 mPendingAnims = new ArrayList<>(1);
768             }
769             mPendingAnims.add(new PendingAnimator(resId, pathErrorScale, target));
770         }
771 
addTargetAnimator(String targetName, Animator animator)772         public void addTargetAnimator(String targetName, Animator animator) {
773             if (mAnimators == null) {
774                 mAnimators = new ArrayList<>(1);
775                 mTargetNameMap = new ArrayMap<>(1);
776             }
777             mAnimators.add(animator);
778             mTargetNameMap.put(animator, targetName);
779 
780             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
781                 Log.v(LOGTAG, "add animator  for target " + targetName + " " + animator);
782             }
783         }
784 
785         /**
786          * Prepares a local set of mutable animators based on the constant
787          * state.
788          * <p>
789          * If there are any pending uninflated animators, attempts to inflate
790          * them immediately against the provided resources object.
791          *
792          * @param animatorSet the animator set to which the animators should
793          *                    be added
794          * @param res the resources against which to inflate any pending
795          *            animators, or {@code null} if not available
796          */
prepareLocalAnimators(@onNull AnimatorSet animatorSet, @Nullable Resources res)797         public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
798                 @Nullable Resources res) {
799             // Check for uninflated animators. We can remove this after we add
800             // support for Animator.applyTheme(). See comments in inflate().
801             if (mPendingAnims != null) {
802                 // Attempt to load animators without applying a theme.
803                 if (res != null) {
804                     inflatePendingAnimators(res, null);
805                 } else {
806                     Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable"
807                             + " must be created using a Resources object or applyTheme() must be"
808                             + " called with a non-null Theme object.");
809                 }
810 
811                 mPendingAnims = null;
812             }
813 
814             // Perform a deep copy of the constant state's animators.
815             final int count = mAnimators == null ? 0 : mAnimators.size();
816             if (count > 0) {
817                 final Animator firstAnim = prepareLocalAnimator(0);
818                 final AnimatorSet.Builder builder = animatorSet.play(firstAnim);
819                 for (int i = 1; i < count; ++i) {
820                     final Animator nextAnim = prepareLocalAnimator(i);
821                     builder.with(nextAnim);
822                 }
823             }
824         }
825 
826         /**
827          * Prepares a local animator for the given index within the constant
828          * state's list of animators.
829          *
830          * @param index the index of the animator within the constant state
831          */
prepareLocalAnimator(int index)832         private Animator prepareLocalAnimator(int index) {
833             final Animator animator = mAnimators.get(index);
834             final Animator localAnimator = animator.clone();
835             final String targetName = mTargetNameMap.get(animator);
836             final Object target = mVectorDrawable.getTargetByName(targetName);
837             if (!mShouldIgnoreInvalidAnim) {
838                 if (target == null) {
839                     throw new IllegalStateException("Target with the name \"" + targetName
840                             + "\" cannot be found in the VectorDrawable to be animated.");
841                 } else if (!(target instanceof VectorDrawable.VectorDrawableState)
842                         && !(target instanceof VectorDrawable.VObject)) {
843                     throw new UnsupportedOperationException("Target should be either VGroup, VPath,"
844                             + " or ConstantState, " + target.getClass() + " is not supported");
845                 }
846             }
847             localAnimator.setTarget(target);
848             return localAnimator;
849         }
850 
851         /**
852          * Inflates pending animators, if any, against a theme. Clears the list of
853          * pending animators.
854          *
855          * @param t the theme against which to inflate the animators
856          */
inflatePendingAnimators(@onNull Resources res, @Nullable Theme t)857         public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) {
858             final ArrayList<PendingAnimator> pendingAnims = mPendingAnims;
859             if (pendingAnims != null) {
860                 mPendingAnims = null;
861 
862                 for (int i = 0, count = pendingAnims.size(); i < count; i++) {
863                     final PendingAnimator pendingAnimator = pendingAnims.get(i);
864                     final Animator animator = pendingAnimator.newInstance(res, t);
865                     updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
866                             mShouldIgnoreInvalidAnim);
867                     addTargetAnimator(pendingAnimator.target, animator);
868                 }
869             }
870         }
871 
872         /**
873          * Basically a constant state for Animators until we actually implement
874          * constant states for Animators.
875          */
876         private static class PendingAnimator {
877             public final int animResId;
878             public final float pathErrorScale;
879             public final String target;
880 
PendingAnimator(int animResId, float pathErrorScale, String target)881             public PendingAnimator(int animResId, float pathErrorScale, String target) {
882                 this.animResId = animResId;
883                 this.pathErrorScale = pathErrorScale;
884                 this.target = target;
885             }
886 
newInstance(Resources res, Theme theme)887             public Animator newInstance(Resources res, Theme theme) {
888                 return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
889             }
890         }
891     }
892 
893     @Override
isRunning()894     public boolean isRunning() {
895         return mAnimatorSet.isRunning();
896     }
897 
898     /**
899      * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
900      */
reset()901     public void reset() {
902         ensureAnimatorSet();
903         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
904             Log.w(LOGTAG, "calling reset on AVD: " +
905                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
906                     getConstantState()).mVectorDrawable.getConstantState()).mRootName
907                     + ", at: " + this);
908         }
909         mAnimatorSet.reset();
910     }
911 
912     @Override
start()913     public void start() {
914         ensureAnimatorSet();
915         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
916             Log.w(LOGTAG, "calling start on AVD: " +
917                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
918                     getConstantState()).mVectorDrawable.getConstantState()).mRootName
919                     + ", at: " + this);
920         }
921         mAnimatorSet.start();
922     }
923 
924     @NonNull
ensureAnimatorSet()925     private void ensureAnimatorSet() {
926         if (mAnimatorSetFromXml == null) {
927             // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
928             // with a list of LocalAnimators.
929             mAnimatorSetFromXml = new AnimatorSet();
930             mAnimatedVectorState.prepareLocalAnimators(mAnimatorSetFromXml, mRes);
931             mAnimatorSet.init(mAnimatorSetFromXml);
932             mRes = null;
933         }
934     }
935 
936     @Override
stop()937     public void stop() {
938         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
939             Log.w(LOGTAG, "calling stop on AVD: " +
940                     ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
941                             getConstantState()).mVectorDrawable.getConstantState())
942                             .mRootName + ", at: " + this);
943         }
944         mAnimatorSet.end();
945     }
946 
947     /**
948      * Reverses ongoing animations or starts pending animations in reverse.
949      * <p>
950      * NOTE: Only works if all animations support reverse. Otherwise, this will
951      * do nothing.
952      * @hide
953      */
reverse()954     public void reverse() {
955         ensureAnimatorSet();
956 
957         // Only reverse when all the animators can be reversed.
958         if (!canReverse()) {
959             Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
960             return;
961         }
962 
963         mAnimatorSet.reverse();
964     }
965 
966     /**
967      * @hide
968      */
canReverse()969     public boolean canReverse() {
970         return mAnimatorSet.canReverse();
971     }
972 
973     private final Callback mCallback = new Callback() {
974         @Override
975         public void invalidateDrawable(@NonNull Drawable who) {
976             invalidateSelf();
977         }
978 
979         @Override
980         public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
981             scheduleSelf(what, when);
982         }
983 
984         @Override
985         public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
986             unscheduleSelf(what);
987         }
988     };
989 
990     @Override
registerAnimationCallback(@onNull AnimationCallback callback)991     public void registerAnimationCallback(@NonNull AnimationCallback callback) {
992         if (callback == null) {
993             return;
994         }
995 
996         // Add listener accordingly.
997         if (mAnimationCallbacks == null) {
998             mAnimationCallbacks = new ArrayList<>();
999         }
1000 
1001         mAnimationCallbacks.add(callback);
1002 
1003         if (mAnimatorListener == null) {
1004             // Create a animator listener and trigger the callback events when listener is
1005             // triggered.
1006             mAnimatorListener = new AnimatorListenerAdapter() {
1007                 @Override
1008                 public void onAnimationStart(Animator animation) {
1009                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1010                     int size = tmpCallbacks.size();
1011                     for (int i = 0; i < size; i ++) {
1012                         tmpCallbacks.get(i).onAnimationStart(AnimatedVectorDrawable.this);
1013                     }
1014                 }
1015 
1016                 @Override
1017                 public void onAnimationEnd(Animator animation) {
1018                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1019                     int size = tmpCallbacks.size();
1020                     for (int i = 0; i < size; i ++) {
1021                         tmpCallbacks.get(i).onAnimationEnd(AnimatedVectorDrawable.this);
1022                     }
1023                 }
1024             };
1025         }
1026         mAnimatorSet.setListener(mAnimatorListener);
1027     }
1028 
1029     // A helper function to clean up the animator listener in the mAnimatorSet.
removeAnimatorSetListener()1030     private void removeAnimatorSetListener() {
1031         if (mAnimatorListener != null) {
1032             mAnimatorSet.removeListener(mAnimatorListener);
1033             mAnimatorListener = null;
1034         }
1035     }
1036 
1037     @Override
unregisterAnimationCallback(@onNull AnimationCallback callback)1038     public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
1039         if (mAnimationCallbacks == null || callback == null) {
1040             // Nothing to be removed.
1041             return false;
1042         }
1043         boolean removed = mAnimationCallbacks.remove(callback);
1044 
1045         //  When the last call back unregistered, remove the listener accordingly.
1046         if (mAnimationCallbacks.size() == 0) {
1047             removeAnimatorSetListener();
1048         }
1049         return removed;
1050     }
1051 
1052     @Override
clearAnimationCallbacks()1053     public void clearAnimationCallbacks() {
1054         removeAnimatorSetListener();
1055         if (mAnimationCallbacks == null) {
1056             return;
1057         }
1058 
1059         mAnimationCallbacks.clear();
1060     }
1061 
1062     private interface VectorDrawableAnimator {
init(@onNull AnimatorSet set)1063         void init(@NonNull AnimatorSet set);
start()1064         void start();
end()1065         void end();
reset()1066         void reset();
reverse()1067         void reverse();
canReverse()1068         boolean canReverse();
setListener(AnimatorListener listener)1069         void setListener(AnimatorListener listener);
removeListener(AnimatorListener listener)1070         void removeListener(AnimatorListener listener);
onDraw(Canvas canvas)1071         void onDraw(Canvas canvas);
isStarted()1072         boolean isStarted();
isRunning()1073         boolean isRunning();
isInfinite()1074         boolean isInfinite();
pause()1075         void pause();
resume()1076         void resume();
1077     }
1078 
1079     private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
1080         // mSet is only initialized in init(). So we need to check whether it is null before any
1081         // operation.
1082         private AnimatorSet mSet = null;
1083         private final Drawable mDrawable;
1084         // Caching the listener in the case when listener operation is called before the mSet is
1085         // setup by init().
1086         private ArrayList<AnimatorListener> mListenerArray = null;
1087         private boolean mIsInfinite = false;
1088 
VectorDrawableAnimatorUI(@onNull AnimatedVectorDrawable drawable)1089         VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
1090             mDrawable = drawable;
1091         }
1092 
1093         @Override
init(@onNull AnimatorSet set)1094         public void init(@NonNull AnimatorSet set) {
1095             if (mSet != null) {
1096                 // Already initialized
1097                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1098                         "re-initialized");
1099             }
1100             // Keep a deep copy of the set, such that set can be still be constantly representing
1101             // the static content from XML file.
1102             mSet = set.clone();
1103             mIsInfinite = mSet.getTotalDuration() == Animator.DURATION_INFINITE;
1104 
1105             // If there are listeners added before calling init(), now they should be setup.
1106             if (mListenerArray != null && !mListenerArray.isEmpty()) {
1107                 for (int i = 0; i < mListenerArray.size(); i++) {
1108                     mSet.addListener(mListenerArray.get(i));
1109                 }
1110                 mListenerArray.clear();
1111                 mListenerArray = null;
1112             }
1113         }
1114 
1115         // Although start(), reset() and reverse() should call init() already, it is better to
1116         // protect these functions from NPE in any situation.
1117         @Override
start()1118         public void start() {
1119             if (mSet == null || mSet.isStarted()) {
1120                 return;
1121             }
1122             mSet.start();
1123             invalidateOwningView();
1124         }
1125 
1126         @Override
end()1127         public void end() {
1128             if (mSet == null) {
1129                 return;
1130             }
1131             mSet.end();
1132         }
1133 
1134         @Override
reset()1135         public void reset() {
1136             if (mSet == null) {
1137                 return;
1138             }
1139             start();
1140             mSet.cancel();
1141         }
1142 
1143         @Override
reverse()1144         public void reverse() {
1145             if (mSet == null) {
1146                 return;
1147             }
1148             mSet.reverse();
1149             invalidateOwningView();
1150         }
1151 
1152         @Override
canReverse()1153         public boolean canReverse() {
1154             return mSet != null && mSet.canReverse();
1155         }
1156 
1157         @Override
setListener(AnimatorListener listener)1158         public void setListener(AnimatorListener listener) {
1159             if (mSet == null) {
1160                 if (mListenerArray == null) {
1161                     mListenerArray = new ArrayList<AnimatorListener>();
1162                 }
1163                 mListenerArray.add(listener);
1164             } else {
1165                 mSet.addListener(listener);
1166             }
1167         }
1168 
1169         @Override
removeListener(AnimatorListener listener)1170         public void removeListener(AnimatorListener listener) {
1171             if (mSet == null) {
1172                 if (mListenerArray == null) {
1173                     return;
1174                 }
1175                 mListenerArray.remove(listener);
1176             } else {
1177                 mSet.removeListener(listener);
1178             }
1179         }
1180 
1181         @Override
onDraw(Canvas canvas)1182         public void onDraw(Canvas canvas) {
1183             if (mSet != null && mSet.isStarted()) {
1184                 invalidateOwningView();
1185             }
1186         }
1187 
1188         @Override
isStarted()1189         public boolean isStarted() {
1190             return mSet != null && mSet.isStarted();
1191         }
1192 
1193         @Override
isRunning()1194         public boolean isRunning() {
1195             return mSet != null && mSet.isRunning();
1196         }
1197 
1198         @Override
isInfinite()1199         public boolean isInfinite() {
1200             return mIsInfinite;
1201         }
1202 
1203         @Override
pause()1204         public void pause() {
1205             if (mSet == null) {
1206                 return;
1207             }
1208             mSet.pause();
1209         }
1210 
1211         @Override
resume()1212         public void resume() {
1213             if (mSet == null) {
1214                 return;
1215             }
1216             mSet.resume();
1217         }
1218 
invalidateOwningView()1219         private void invalidateOwningView() {
1220             mDrawable.invalidateSelf();
1221         }
1222     }
1223 
1224     /**
1225      * @hide
1226      */
1227     public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator,
1228             NativeVectorDrawableAnimator {
1229         private static final int START_ANIMATION = 1;
1230         private static final int REVERSE_ANIMATION = 2;
1231         private static final int RESET_ANIMATION = 3;
1232         private static final int END_ANIMATION = 4;
1233 
1234         // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
1235         private static final int MAX_SAMPLE_POINTS = 300;
1236         private Handler mHandler;
1237         private AnimatorListener mListener = null;
1238         private final LongArray mStartDelays = new LongArray();
1239         private PropertyValuesHolder.PropertyValues mTmpValues =
1240                 new PropertyValuesHolder.PropertyValues();
1241         private long mSetPtr = 0;
1242         private boolean mContainsSequentialAnimators = false;
1243         private boolean mStarted = false;
1244         private boolean mInitialized = false;
1245         private boolean mIsReversible = false;
1246         private boolean mIsInfinite = false;
1247         private final VirtualRefBasePtr mSetRefBasePtr;
1248         private WeakReference<RenderNode> mLastSeenTarget = null;
1249         private int mLastListenerId = 0;
1250         private final IntArray mPendingAnimationActions = new IntArray();
1251         private final AnimatedVectorDrawable mDrawable;
1252 
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable)1253         VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
1254             mDrawable = drawable;
1255             mSetPtr = nCreateAnimatorSet();
1256             // Increment ref count on native AnimatorSet, so it doesn't get released before Java
1257             // side is done using it.
1258             mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
1259         }
1260 
1261         @Override
init(@onNull AnimatorSet set)1262         public void init(@NonNull AnimatorSet set) {
1263             if (mInitialized) {
1264                 // Already initialized
1265                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1266                         "re-initialized");
1267             }
1268             parseAnimatorSet(set, 0);
1269             long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
1270                     .getNativeTree();
1271             nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
1272             mInitialized = true;
1273             mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
1274 
1275             // Check reversible.
1276             mIsReversible = true;
1277             if (mContainsSequentialAnimators) {
1278                 mIsReversible = false;
1279             } else {
1280                 // Check if there's any start delay set on child
1281                 for (int i = 0; i < mStartDelays.size(); i++) {
1282                     if (mStartDelays.get(i) > 0) {
1283                         mIsReversible = false;
1284                         return;
1285                     }
1286                 }
1287             }
1288         }
1289 
parseAnimatorSet(AnimatorSet set, long startTime)1290         private void parseAnimatorSet(AnimatorSet set, long startTime) {
1291             ArrayList<Animator> animators = set.getChildAnimations();
1292 
1293             boolean playTogether = set.shouldPlayTogether();
1294             // Convert AnimatorSet to VectorDrawableAnimatorRT
1295             for (int i = 0; i < animators.size(); i++) {
1296                 Animator animator = animators.get(i);
1297                 // Here we only support ObjectAnimator
1298                 if (animator instanceof AnimatorSet) {
1299                     parseAnimatorSet((AnimatorSet) animator, startTime);
1300                 } else if (animator instanceof ObjectAnimator) {
1301                     createRTAnimator((ObjectAnimator) animator, startTime);
1302                 } // ignore ValueAnimators and others because they don't directly modify VD
1303                   // therefore will be useless to AVD.
1304 
1305                 if (!playTogether) {
1306                     // Assume not play together means play sequentially
1307                     startTime += animator.getTotalDuration();
1308                     mContainsSequentialAnimators = true;
1309                 }
1310             }
1311         }
1312 
1313         // TODO: This method reads animation data from already parsed Animators. We need to move
1314         // this step further up the chain in the parser to avoid the detour.
createRTAnimator(ObjectAnimator animator, long startTime)1315         private void createRTAnimator(ObjectAnimator animator, long startTime) {
1316             PropertyValuesHolder[] values = animator.getValues();
1317             Object target = animator.getTarget();
1318             if (target instanceof VectorDrawable.VGroup) {
1319                 createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
1320                         startTime);
1321             } else if (target instanceof VectorDrawable.VPath) {
1322                 for (int i = 0; i < values.length; i++) {
1323                     values[i].getPropertyValues(mTmpValues);
1324                     if (mTmpValues.endValue instanceof PathParser.PathData &&
1325                             mTmpValues.propertyName.equals("pathData")) {
1326                         createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
1327                                 startTime);
1328                     }  else if (target instanceof VectorDrawable.VFullPath) {
1329                         createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
1330                                 startTime);
1331                     } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1332                         throw new IllegalArgumentException("ClipPath only supports PathData " +
1333                                 "property");
1334                     }
1335                 }
1336             } else if (target instanceof VectorDrawable.VectorDrawableState) {
1337                 createRTAnimatorForRootGroup(values, animator,
1338                         (VectorDrawable.VectorDrawableState) target, startTime);
1339             }
1340         }
1341 
createRTAnimatorForGroup(PropertyValuesHolder[] values, ObjectAnimator animator, VectorDrawable.VGroup target, long startTime)1342         private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
1343                 ObjectAnimator animator, VectorDrawable.VGroup target,
1344                 long startTime) {
1345 
1346             long nativePtr = target.getNativePtr();
1347             int propertyId;
1348             for (int i = 0; i < values.length; i++) {
1349                 // TODO: We need to support the rare case in AVD where no start value is provided
1350                 values[i].getPropertyValues(mTmpValues);
1351                 propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
1352                 if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
1353                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1354                         Log.e(LOGTAG, "Unsupported type: " +
1355                                 mTmpValues.type + ". Only float value is supported for Groups.");
1356                     }
1357                     continue;
1358                 }
1359                 if (propertyId < 0) {
1360                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1361                         Log.e(LOGTAG, "Unsupported property: " +
1362                                 mTmpValues.propertyName + " for Vector Drawable Group");
1363                     }
1364                     continue;
1365                 }
1366                 long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
1367                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1368                 if (mTmpValues.dataSource != null) {
1369                     float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1370                             animator.getDuration());
1371                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1372                 }
1373                 createNativeChildAnimator(propertyPtr, startTime, animator);
1374             }
1375         }
createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target, long startTime)1376         private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
1377                 long startTime) {
1378 
1379             long nativePtr = target.getNativePtr();
1380             long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
1381                     .getNativePtr();
1382             long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
1383                     .getNativePtr();
1384             long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
1385                     endPathDataPtr);
1386             createNativeChildAnimator(propertyPtr, startTime, animator);
1387         }
1388 
createRTAnimatorForFullPath(ObjectAnimator animator, VectorDrawable.VFullPath target, long startTime)1389         private void createRTAnimatorForFullPath(ObjectAnimator animator,
1390                 VectorDrawable.VFullPath target, long startTime) {
1391 
1392             int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
1393             long propertyPtr;
1394             long nativePtr = target.getNativePtr();
1395             if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
1396                 if (propertyId < 0) {
1397                     if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1398                         return;
1399                     } else {
1400                         throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
1401                                 + " is not supported for FullPath");
1402                     }
1403                 }
1404                 propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
1405                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1406                 if (mTmpValues.dataSource != null) {
1407                     // Pass keyframe data to native, if any.
1408                     float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1409                             animator.getDuration());
1410                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1411                 }
1412 
1413             } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
1414                 propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
1415                         (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
1416                 if (mTmpValues.dataSource != null) {
1417                     // Pass keyframe data to native, if any.
1418                     int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
1419                             animator.getDuration());
1420                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1421                 }
1422             } else {
1423                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1424                     return;
1425                 } else {
1426                     throw new UnsupportedOperationException("Unsupported type: " +
1427                             mTmpValues.type + ". Only float, int or PathData value is " +
1428                             "supported for Paths.");
1429                 }
1430             }
1431             createNativeChildAnimator(propertyPtr, startTime, animator);
1432         }
1433 
createRTAnimatorForRootGroup(PropertyValuesHolder[] values, ObjectAnimator animator, VectorDrawable.VectorDrawableState target, long startTime)1434         private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
1435                 ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
1436                 long startTime) {
1437             long nativePtr = target.getNativeRenderer();
1438             if (!animator.getPropertyName().equals("alpha")) {
1439                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1440                     return;
1441                 } else {
1442                     throw new UnsupportedOperationException("Only alpha is supported for root "
1443                             + "group");
1444                 }
1445             }
1446             Float startValue = null;
1447             Float endValue = null;
1448             for (int i = 0; i < values.length; i++) {
1449                 values[i].getPropertyValues(mTmpValues);
1450                 if (mTmpValues.propertyName.equals("alpha")) {
1451                     startValue = (Float) mTmpValues.startValue;
1452                     endValue = (Float) mTmpValues.endValue;
1453                     break;
1454                 }
1455             }
1456             if (startValue == null && endValue == null) {
1457                 if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1458                     return;
1459                 } else {
1460                     throw new UnsupportedOperationException("No alpha values are specified");
1461                 }
1462             }
1463             long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
1464             if (mTmpValues.dataSource != null) {
1465                 // Pass keyframe data to native, if any.
1466                 float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1467                         animator.getDuration());
1468                 nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1469             }
1470             createNativeChildAnimator(propertyPtr, startTime, animator);
1471         }
1472 
1473         /**
1474          * Calculate the amount of frames an animation will run based on duration.
1475          */
getFrameCount(long duration)1476         private static int getFrameCount(long duration) {
1477             long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
1478             int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
1479             int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
1480             // We need 2 frames of data minimum.
1481             numAnimFrames = Math.max(2, numAnimFrames);
1482             if (numAnimFrames > MAX_SAMPLE_POINTS) {
1483                 Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
1484                         duration + ", the animation will subsample the keyframe or path data.");
1485                 numAnimFrames = MAX_SAMPLE_POINTS;
1486             }
1487             return numAnimFrames;
1488         }
1489 
1490         // These are the data points that define the value of the animating properties.
1491         // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
1492         // a point on the path corresponds to the values of translateX and translateY.
1493         // TODO: (Optimization) We should pass the path down in native and chop it into segments
1494         // in native.
createFloatDataPoints( PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration)1495         private static float[] createFloatDataPoints(
1496                 PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1497             int numAnimFrames = getFrameCount(duration);
1498             float values[] = new float[numAnimFrames];
1499             float lastFrame = numAnimFrames - 1;
1500             for (int i = 0; i < numAnimFrames; i++) {
1501                 float fraction = i / lastFrame;
1502                 values[i] = (Float) dataSource.getValueAtFraction(fraction);
1503             }
1504             return values;
1505         }
1506 
createIntDataPoints( PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration)1507         private static int[] createIntDataPoints(
1508                 PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1509             int numAnimFrames = getFrameCount(duration);
1510             int values[] = new int[numAnimFrames];
1511             float lastFrame = numAnimFrames - 1;
1512             for (int i = 0; i < numAnimFrames; i++) {
1513                 float fraction = i / lastFrame;
1514                 values[i] = (Integer) dataSource.getValueAtFraction(fraction);
1515             }
1516             return values;
1517         }
1518 
createNativeChildAnimator(long propertyPtr, long extraDelay, ObjectAnimator animator)1519         private void createNativeChildAnimator(long propertyPtr, long extraDelay,
1520                                                ObjectAnimator animator) {
1521             long duration = animator.getDuration();
1522             int repeatCount = animator.getRepeatCount();
1523             long startDelay = extraDelay + animator.getStartDelay();
1524             TimeInterpolator interpolator = animator.getInterpolator();
1525             long nativeInterpolator =
1526                     NativeInterpolatorFactory.createNativeInterpolator(interpolator, duration);
1527 
1528             startDelay *= ValueAnimator.getDurationScale();
1529             duration *= ValueAnimator.getDurationScale();
1530 
1531             mStartDelays.add(startDelay);
1532             nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
1533                     repeatCount, animator.getRepeatMode());
1534         }
1535 
1536         /**
1537          * Holds a weak reference to the target that was last seen (through the RecordingCanvas
1538          * in the last draw call), so that when animator set needs to start, we can add the animator
1539          * to the last seen RenderNode target and start right away.
1540          */
recordLastSeenTarget(RecordingCanvas canvas)1541         protected void recordLastSeenTarget(RecordingCanvas canvas) {
1542             final RenderNode node = canvas.mNode;
1543             mLastSeenTarget = new WeakReference<RenderNode>(node);
1544             // Add the animator to the list of animators on every draw
1545             if (mInitialized || mPendingAnimationActions.size() > 0) {
1546                 if (useTarget(node)) {
1547                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1548                         Log.d(LOGTAG, "Target is set in the next frame");
1549                     }
1550                     for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1551                         handlePendingAction(mPendingAnimationActions.get(i));
1552                     }
1553                     mPendingAnimationActions.clear();
1554                 }
1555             }
1556         }
1557 
handlePendingAction(int pendingAnimationAction)1558         private void handlePendingAction(int pendingAnimationAction) {
1559             if (pendingAnimationAction == START_ANIMATION) {
1560                 startAnimation();
1561             } else if (pendingAnimationAction == REVERSE_ANIMATION) {
1562                 reverseAnimation();
1563             } else if (pendingAnimationAction == RESET_ANIMATION) {
1564                 resetAnimation();
1565             } else if (pendingAnimationAction == END_ANIMATION) {
1566                 endAnimation();
1567             } else {
1568                 throw new UnsupportedOperationException("Animation action " +
1569                         pendingAnimationAction + "is not supported");
1570             }
1571         }
1572 
useLastSeenTarget()1573         private boolean useLastSeenTarget() {
1574             if (mLastSeenTarget != null) {
1575                 final RenderNode target = mLastSeenTarget.get();
1576                 return useTarget(target);
1577             }
1578             return false;
1579         }
1580 
useTarget(RenderNode target)1581         private boolean useTarget(RenderNode target) {
1582             if (target != null && target.isAttached()) {
1583                 target.registerVectorDrawableAnimator(this);
1584                 return true;
1585             }
1586             return false;
1587         }
1588 
invalidateOwningView()1589         private void invalidateOwningView() {
1590             mDrawable.invalidateSelf();
1591         }
1592 
addPendingAction(int pendingAnimationAction)1593         private void addPendingAction(int pendingAnimationAction) {
1594             invalidateOwningView();
1595             mPendingAnimationActions.add(pendingAnimationAction);
1596         }
1597 
1598         @Override
start()1599         public void start() {
1600             if (!mInitialized) {
1601                 return;
1602             }
1603 
1604             if (useLastSeenTarget()) {
1605                 if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1606                     Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
1607                 }
1608                 startAnimation();
1609             } else {
1610                 addPendingAction(START_ANIMATION);
1611             }
1612         }
1613 
1614         @Override
end()1615         public void end() {
1616             if (!mInitialized) {
1617                 return;
1618             }
1619 
1620             if (useLastSeenTarget()) {
1621                 endAnimation();
1622             } else {
1623                 addPendingAction(END_ANIMATION);
1624             }
1625         }
1626 
1627         @Override
reset()1628         public void reset() {
1629             if (!mInitialized) {
1630                 return;
1631             }
1632 
1633             if (useLastSeenTarget()) {
1634                 resetAnimation();
1635             } else {
1636                 addPendingAction(RESET_ANIMATION);
1637             }
1638         }
1639 
1640         // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
1641         // animators or when the animator set has a start delay
1642         @Override
reverse()1643         public void reverse() {
1644             if (!mIsReversible || !mInitialized) {
1645                 return;
1646             }
1647             if (useLastSeenTarget()) {
1648                 if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1649                     Log.d(LOGTAG, "Target is set. Reversing VDAnimatorSet from java");
1650                 }
1651                 reverseAnimation();
1652             } else {
1653                 addPendingAction(REVERSE_ANIMATION);
1654             }
1655         }
1656 
1657         // This should only be called after animator has been added to the RenderNode target.
startAnimation()1658         private void startAnimation() {
1659             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1660                 Log.w(LOGTAG, "starting animation on VD: " +
1661                         ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1662                                 mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1663                                 .mRootName);
1664             }
1665             mStarted = true;
1666             if (mHandler == null) {
1667                 mHandler = new Handler();
1668             }
1669             nStart(mSetPtr, this, ++mLastListenerId);
1670             invalidateOwningView();
1671             if (mListener != null) {
1672                 mListener.onAnimationStart(null);
1673             }
1674         }
1675 
1676         // This should only be called after animator has been added to the RenderNode target.
endAnimation()1677         private void endAnimation() {
1678             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1679                 Log.w(LOGTAG, "ending animation on VD: " +
1680                         ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1681                                 mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1682                                 .mRootName);
1683             }
1684             nEnd(mSetPtr);
1685             invalidateOwningView();
1686         }
1687 
1688         // This should only be called after animator has been added to the RenderNode target.
resetAnimation()1689         private void resetAnimation() {
1690             nReset(mSetPtr);
1691             invalidateOwningView();
1692         }
1693 
1694         // This should only be called after animator has been added to the RenderNode target.
reverseAnimation()1695         private void reverseAnimation() {
1696             mStarted = true;
1697             nReverse(mSetPtr, this, ++mLastListenerId);
1698             invalidateOwningView();
1699             if (mListener != null) {
1700                 mListener.onAnimationStart(null);
1701             }
1702         }
1703 
1704         @Override
getAnimatorNativePtr()1705         public long getAnimatorNativePtr() {
1706             return mSetPtr;
1707         }
1708 
1709         @Override
canReverse()1710         public boolean canReverse() {
1711             return mIsReversible;
1712         }
1713 
1714         @Override
isStarted()1715         public boolean isStarted() {
1716             return mStarted;
1717         }
1718 
1719         @Override
isRunning()1720         public boolean isRunning() {
1721             if (!mInitialized) {
1722                 return false;
1723             }
1724             return mStarted;
1725         }
1726 
1727         @Override
setListener(AnimatorListener listener)1728         public void setListener(AnimatorListener listener) {
1729             mListener = listener;
1730         }
1731 
1732         @Override
removeListener(AnimatorListener listener)1733         public void removeListener(AnimatorListener listener) {
1734             mListener = null;
1735         }
1736 
1737         @Override
onDraw(Canvas canvas)1738         public void onDraw(Canvas canvas) {
1739             if (canvas.isHardwareAccelerated()) {
1740                 recordLastSeenTarget((RecordingCanvas) canvas);
1741             }
1742         }
1743 
1744         @Override
isInfinite()1745         public boolean isInfinite() {
1746             return mIsInfinite;
1747         }
1748 
1749         @Override
pause()1750         public void pause() {
1751             // TODO: Implement pause for Animator On RT.
1752         }
1753 
1754         @Override
resume()1755         public void resume() {
1756             // TODO: Implement resume for Animator On RT.
1757         }
1758 
onAnimationEnd(int listenerId)1759         private void onAnimationEnd(int listenerId) {
1760             if (listenerId != mLastListenerId) {
1761                 return;
1762             }
1763             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1764                 Log.d(LOGTAG, "on finished called from native");
1765             }
1766             mStarted = false;
1767             // Invalidate in the end of the animation to make sure the data in
1768             // RT thread is synced back to UI thread.
1769             invalidateOwningView();
1770             if (mListener != null) {
1771                 mListener.onAnimationEnd(null);
1772             }
1773         }
1774 
1775         // onFinished: should be called from native
1776         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
callOnFinished(VectorDrawableAnimatorRT set, int id)1777         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
1778             set.mHandler.post(() -> set.onAnimationEnd(id));
1779         }
1780 
transferPendingActions(VectorDrawableAnimator animatorSet)1781         private void transferPendingActions(VectorDrawableAnimator animatorSet) {
1782             for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1783                 int pendingAction = mPendingAnimationActions.get(i);
1784                 if (pendingAction == START_ANIMATION) {
1785                     animatorSet.start();
1786                 } else if (pendingAction == END_ANIMATION) {
1787                     animatorSet.end();
1788                 } else if (pendingAction == REVERSE_ANIMATION) {
1789                     animatorSet.reverse();
1790                 } else if (pendingAction == RESET_ANIMATION) {
1791                     animatorSet.reset();
1792                 } else {
1793                     throw new UnsupportedOperationException("Animation action " +
1794                             pendingAction + "is not supported");
1795                 }
1796             }
1797             mPendingAnimationActions.clear();
1798         }
1799     }
1800 
nCreateAnimatorSet()1801     private static native long nCreateAnimatorSet();
nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr)1802     private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
nAddAnimator(long setPtr, long propertyValuesHolder, long nativeInterpolator, long startDelay, long duration, int repeatCount, int repeatMode)1803     private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
1804             long nativeInterpolator, long startDelay, long duration, int repeatCount,
1805             int repeatMode);
nSetPropertyHolderData(long nativePtr, float[] data, int length)1806     private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
nSetPropertyHolderData(long nativePtr, int[] data, int length)1807     private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id)1808     private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id)1809     private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
1810 
1811     // ------------- @FastNative -------------------
1812 
1813     @FastNative
nCreateGroupPropertyHolder(long nativePtr, int propertyId, float startValue, float endValue)1814     private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
1815             float startValue, float endValue);
1816     @FastNative
nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr, long endValuePtr)1817     private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
1818             long endValuePtr);
1819     @FastNative
nCreatePathColorPropertyHolder(long nativePtr, int propertyId, int startValue, int endValue)1820     private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
1821             int startValue, int endValue);
1822     @FastNative
nCreatePathPropertyHolder(long nativePtr, int propertyId, float startValue, float endValue)1823     private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
1824             float startValue, float endValue);
1825     @FastNative
nCreateRootAlphaPropertyHolder(long nativePtr, float startValue, float endValue)1826     private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
1827             float endValue);
1828     @FastNative
nEnd(long animatorSetPtr)1829     private static native void nEnd(long animatorSetPtr);
1830     @FastNative
nReset(long animatorSetPtr)1831     private static native void nReset(long animatorSetPtr);
1832 }
1833