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