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