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