Home | History | Annotate | Download | only in drawable
      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";
    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";
    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">;
    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";
    250  *                  xmlns:aapt=&quothttp://schemas.android.com/aapt"; &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 
    327     public AnimatedVectorDrawable() {
    328         this(null, null);
    329     }
    330 
    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
    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      */
    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      */
    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
    377     public ConstantState getConstantState() {
    378         mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
    379         return mAnimatedVectorState;
    380     }
    381 
    382     @Override
    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
    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
    413     protected void onBoundsChange(Rect bounds) {
    414         mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
    415     }
    416 
    417     @Override
    418     protected boolean onStateChange(int[] state) {
    419         return mAnimatedVectorState.mVectorDrawable.setState(state);
    420     }
    421 
    422     @Override
    423     protected boolean onLevelChange(int level) {
    424         return mAnimatedVectorState.mVectorDrawable.setLevel(level);
    425     }
    426 
    427     @Override
    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
    440     public int getAlpha() {
    441         return mAnimatedVectorState.mVectorDrawable.getAlpha();
    442     }
    443 
    444     @Override
    445     public void setAlpha(int alpha) {
    446         mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
    447     }
    448 
    449     @Override
    450     public void setColorFilter(ColorFilter colorFilter) {
    451         mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
    452     }
    453 
    454     @Override
    455     public ColorFilter getColorFilter() {
    456         return mAnimatedVectorState.mVectorDrawable.getColorFilter();
    457     }
    458 
    459     @Override
    460     public void setTintList(ColorStateList tint) {
    461         mAnimatedVectorState.mVectorDrawable.setTintList(tint);
    462     }
    463 
    464     @Override
    465     public void setHotspot(float x, float y) {
    466         mAnimatedVectorState.mVectorDrawable.setHotspot(x, y);
    467     }
    468 
    469     @Override
    470     public void setHotspotBounds(int left, int top, int right, int bottom) {
    471         mAnimatedVectorState.mVectorDrawable.setHotspotBounds(left, top, right, bottom);
    472     }
    473 
    474     @Override
    475     public void setTintMode(PorterDuff.Mode tintMode) {
    476         mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
    477     }
    478 
    479     @Override
    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
    495     public boolean isStateful() {
    496         return mAnimatedVectorState.mVectorDrawable.isStateful();
    497     }
    498 
    499     @Override
    500     public int getOpacity() {
    501         return PixelFormat.TRANSLUCENT;
    502     }
    503 
    504     @Override
    505     public int getIntrinsicWidth() {
    506         return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
    507     }
    508 
    509     @Override
    510     public int getIntrinsicHeight() {
    511         return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
    512     }
    513 
    514     @Override
    515     public void getOutline(@NonNull Outline outline) {
    516         mAnimatedVectorState.mVectorDrawable.getOutline(outline);
    517     }
    518 
    519     /** @hide */
    520     @Override
    521     public Insets getOpticalInsets() {
    522         return mAnimatedVectorState.mVectorDrawable.getOpticalInsets();
    523     }
    524 
    525     @Override
    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 
    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 
    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      */
    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 
    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
    672     public boolean canApplyTheme() {
    673         return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme())
    674                 || super.canApplyTheme();
    675     }
    676 
    677     @Override
    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 
    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
    749         public boolean canApplyTheme() {
    750             return (mVectorDrawable != null && mVectorDrawable.canApplyTheme())
    751                     || mPendingAnims != null || super.canApplyTheme();
    752         }
    753 
    754         @Override
    755         public Drawable newDrawable() {
    756             return new AnimatedVectorDrawable(this, null);
    757         }
    758 
    759         @Override
    760         public Drawable newDrawable(Resources res) {
    761             return new AnimatedVectorDrawable(this, res);
    762         }
    763 
    764         @Override
    765         public @Config int getChangingConfigurations() {
    766             return mChangingConfigurations;
    767         }
    768 
    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 
    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          */
    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          */
    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          */
    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 
    885             public PendingAnimator(int animResId, float pathErrorScale, String target) {
    886                 this.animResId = animResId;
    887                 this.pathErrorScale = pathErrorScale;
    888                 this.target = target;
    889             }
    890 
    891             public Animator newInstance(Resources res, Theme theme) {
    892                 return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
    893             }
    894         }
    895     }
    896 
    897     @Override
    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      */
    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
    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
    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
    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      */
    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      */
    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
    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.
   1034     private void removeAnimatorSetListener() {
   1035         if (mAnimatorListener != null) {
   1036             mAnimatorSet.removeListener(mAnimatorListener);
   1037             mAnimatorListener = null;
   1038         }
   1039     }
   1040 
   1041     @Override
   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
   1057     public void clearAnimationCallbacks() {
   1058         removeAnimatorSetListener();
   1059         if (mAnimationCallbacks == null) {
   1060             return;
   1061         }
   1062 
   1063         mAnimationCallbacks.clear();
   1064     }
   1065 
   1066     private interface VectorDrawableAnimator {
   1067         void init(@NonNull AnimatorSet set);
   1068         void start();
   1069         void end();
   1070         void reset();
   1071         void reverse();
   1072         boolean canReverse();
   1073         void setListener(AnimatorListener listener);
   1074         void removeListener(AnimatorListener listener);
   1075         void onDraw(Canvas canvas);
   1076         boolean isStarted();
   1077         boolean isRunning();
   1078         boolean isInfinite();
   1079         void pause();
   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 
   1093         VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
   1094             mDrawable = drawable;
   1095         }
   1096 
   1097         @Override
   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
   1122         public void start() {
   1123             if (mSet == null || mSet.isStarted()) {
   1124                 return;
   1125             }
   1126             mSet.start();
   1127             invalidateOwningView();
   1128         }
   1129 
   1130         @Override
   1131         public void end() {
   1132             if (mSet == null) {
   1133                 return;
   1134             }
   1135             mSet.end();
   1136         }
   1137 
   1138         @Override
   1139         public void reset() {
   1140             if (mSet == null) {
   1141                 return;
   1142             }
   1143             start();
   1144             mSet.cancel();
   1145         }
   1146 
   1147         @Override
   1148         public void reverse() {
   1149             if (mSet == null) {
   1150                 return;
   1151             }
   1152             mSet.reverse();
   1153             invalidateOwningView();
   1154         }
   1155 
   1156         @Override
   1157         public boolean canReverse() {
   1158             return mSet != null && mSet.canReverse();
   1159         }
   1160 
   1161         @Override
   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
   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
   1186         public void onDraw(Canvas canvas) {
   1187             if (mSet != null && mSet.isStarted()) {
   1188                 invalidateOwningView();
   1189             }
   1190         }
   1191 
   1192         @Override
   1193         public boolean isStarted() {
   1194             return mSet != null && mSet.isStarted();
   1195         }
   1196 
   1197         @Override
   1198         public boolean isRunning() {
   1199             return mSet != null && mSet.isRunning();
   1200         }
   1201 
   1202         @Override
   1203         public boolean isInfinite() {
   1204             return mIsInfinite;
   1205         }
   1206 
   1207         @Override
   1208         public void pause() {
   1209             if (mSet == null) {
   1210                 return;
   1211             }
   1212             mSet.pause();
   1213         }
   1214 
   1215         @Override
   1216         public void resume() {
   1217             if (mSet == null) {
   1218                 return;
   1219             }
   1220             mSet.resume();
   1221         }
   1222 
   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 
   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
   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 
   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.
   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 
   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         }
   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 
   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 
   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          */
   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.
   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 
   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 
   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          */
   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 
   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 
   1576         private boolean useLastSeenTarget() {
   1577             if (mLastSeenTarget != null) {
   1578                 final RenderNode target = mLastSeenTarget.get();
   1579                 return useTarget(target);
   1580             }
   1581             return false;
   1582         }
   1583 
   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 
   1592         private void invalidateOwningView() {
   1593             mDrawable.invalidateSelf();
   1594         }
   1595 
   1596         private void addPendingAction(int pendingAnimationAction) {
   1597             invalidateOwningView();
   1598             mPendingAnimationActions.add(pendingAnimationAction);
   1599         }
   1600 
   1601         @Override
   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
   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
   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
   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.
   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.
   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.
   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.
   1695         private void reverseAnimation() {
   1696             mStarted = true;
   1697             nReverse(mSetPtr, this, ++mLastListenerId);
   1698             invalidateOwningView();
   1699             if (mListener != null) {
   1700                 mListener.onAnimationStart(null);
   1701             }
   1702         }
   1703 
   1704         public long getAnimatorNativePtr() {
   1705             return mSetPtr;
   1706         }
   1707 
   1708         @Override
   1709         public boolean canReverse() {
   1710             return mIsReversible;
   1711         }
   1712 
   1713         @Override
   1714         public boolean isStarted() {
   1715             return mStarted;
   1716         }
   1717 
   1718         @Override
   1719         public boolean isRunning() {
   1720             if (!mInitialized) {
   1721                 return false;
   1722             }
   1723             return mStarted;
   1724         }
   1725 
   1726         @Override
   1727         public void setListener(AnimatorListener listener) {
   1728             mListener = listener;
   1729         }
   1730 
   1731         @Override
   1732         public void removeListener(AnimatorListener listener) {
   1733             mListener = null;
   1734         }
   1735 
   1736         @Override
   1737         public void onDraw(Canvas canvas) {
   1738             if (canvas.isHardwareAccelerated()) {
   1739                 recordLastSeenTarget((DisplayListCanvas) canvas);
   1740             }
   1741         }
   1742 
   1743         @Override
   1744         public boolean isInfinite() {
   1745             return mIsInfinite;
   1746         }
   1747 
   1748         @Override
   1749         public void pause() {
   1750             // TODO: Implement pause for Animator On RT.
   1751         }
   1752 
   1753         @Override
   1754         public void resume() {
   1755             // TODO: Implement resume for Animator On RT.
   1756         }
   1757 
   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
   1775         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
   1776             set.onAnimationEnd(id);
   1777         }
   1778 
   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 
   1799     private static native long nCreateAnimatorSet();
   1800     private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
   1801     private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
   1802             long nativeInterpolator, long startDelay, long duration, int repeatCount,
   1803             int repeatMode);
   1804     private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
   1805     private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
   1806     private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
   1807     private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
   1808 
   1809     // ------------- @FastNative -------------------
   1810 
   1811     @FastNative
   1812     private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
   1813             float startValue, float endValue);
   1814     @FastNative
   1815     private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
   1816             long endValuePtr);
   1817     @FastNative
   1818     private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
   1819             int startValue, int endValue);
   1820     @FastNative
   1821     private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
   1822             float startValue, float endValue);
   1823     @FastNative
   1824     private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
   1825             float endValue);
   1826     @FastNative
   1827     private static native void nEnd(long animatorSetPtr);
   1828     @FastNative
   1829     private static native void nReset(long animatorSetPtr);
   1830 }
   1831