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