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