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