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