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