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