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