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