AnimatedVectorDrawableCompat.java revision 408cb9f78c1bedffac5fabbc59c8cbf1932d3610
1/*
2 * Copyright (C) 2015 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.support.graphics.drawable;
16
17import android.animation.Animator;
18import android.animation.AnimatorInflater;
19import android.animation.AnimatorSet;
20import android.animation.ArgbEvaluator;
21import android.animation.ObjectAnimator;
22import android.annotation.TargetApi;
23import android.content.Context;
24import android.content.res.ColorStateList;
25import android.content.res.Resources;
26import android.content.res.Resources.Theme;
27import android.content.res.TypedArray;
28import android.graphics.Canvas;
29import android.graphics.ColorFilter;
30import android.graphics.PorterDuff;
31import android.graphics.Rect;
32import android.graphics.drawable.Animatable;
33import android.graphics.drawable.AnimatedVectorDrawable;
34import android.graphics.drawable.Drawable;
35import android.os.Build;
36import android.support.annotation.DrawableRes;
37import android.support.annotation.NonNull;
38import android.support.annotation.Nullable;
39import android.support.v4.content.res.ResourcesCompat;
40import android.support.v4.graphics.drawable.DrawableCompat;
41import android.support.v4.util.ArrayMap;
42import android.util.AttributeSet;
43import android.util.Log;
44import android.util.Xml;
45import org.xmlpull.v1.XmlPullParser;
46import org.xmlpull.v1.XmlPullParserException;
47
48import java.io.IOException;
49import java.util.ArrayList;
50import java.util.List;
51
52/**
53 * This class uses {@link android.animation.ObjectAnimator} and
54 * {@link android.animation.AnimatorSet} to animate the properties of a
55 * {@link VectorDrawableCompat} to create an animated drawable.
56 * <p>
57 * AnimatedVectorDrawableCompat are normally defined as 3 separate XML files.
58 * </p>
59 * <p>
60 * First is the XML file for {@link VectorDrawableCompat}. Note that we
61 * allow the animation to happen on the group's attributes and path's attributes, which requires they
62 * are uniquely named in this XML file. Groups and paths without animations do not need names.
63 * </p>
64 * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
65 * <pre>
66 * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android";
67 *     android:height=&quot;64dp&quot;
68 *     android:width=&quot;64dp&quot;
69 *     android:viewportHeight=&quot;600&quot;
70 *     android:viewportWidth=&quot;600&quot; &gt;
71 *     &lt;group
72 *         android:name=&quot;rotationGroup&quot;
73 *         android:pivotX=&quot;300.0&quot;
74 *         android:pivotY=&quot;300.0&quot;
75 *         android:rotation=&quot;45.0&quot; &gt;
76 *         &lt;path
77 *             android:name=&quot;v&quot;
78 *             android:fillColor=&quot;#000000&quot;
79 *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
80 *     &lt;/group&gt;
81 * &lt;/vector&gt;
82 * </pre></li>
83 * <p>
84 * Second is the AnimatedVectorDrawableCompat's XML file, which defines the target
85 * VectorDrawableCompat, the target paths and groups to animate, the properties of the path and
86 * group to animate and the animations defined as the ObjectAnimators or AnimatorSets.
87 * </p>
88 * <li>Here is a simple AnimatedVectorDrawable defined in this avd.xml file.
89 * Note how we use the names to refer to the groups and paths in the vectordrawable.xml.
90 * <pre>
91 * &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android";
92 *   android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
93 *     &lt;target
94 *         android:name=&quot;rotationGroup&quot;
95 *         android:animation=&quot;@anim/rotation&quot; /&gt;
96 *     &lt;target
97 *         android:name=&quot;v&quot;
98 *         android:animation=&quot;@anim/path_morph&quot; /&gt;
99 * &lt;/animated-vector&gt;
100 * </pre></li>
101 * <p>
102 * Last is the Animator XML file, which is the same as a normal ObjectAnimator or AnimatorSet. To
103 * complete this example, here are the 2 animator files used in avd.xml: rotation.xml and
104 * path_morph.xml.
105 * </p>
106 * <li>Here is the rotation.xml, which will rotate the target group for 360 degrees.
107 * <pre>
108 * &lt;objectAnimator
109 *     android:duration=&quot;6000&quot;
110 *     android:propertyName=&quot;rotation&quot;
111 *     android:valueFrom=&quot;0&quot;
112 *     android:valueTo=&quot;360&quot; /&gt;
113 * </pre></li>
114 * <li>Here is the path_morph.xml, which will morph the path from one shape to
115 * the other. Note that the paths must be compatible for morphing.
116 * In more details, the paths should have exact same length of commands, and
117 * exact same length of parameters for each commands.
118 * Note that the path strings are better stored in strings.xml for reusing.
119 * <pre>
120 * &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android">;
121 *     &lt;objectAnimator
122 *         android:duration=&quot;3000&quot;
123 *         android:propertyName=&quot;pathData&quot;
124 *         android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0   -70,70z&quot;
125 *         android:valueTo=&quot;M300,70 l 0,-70 70,0  0,140 -70,0 z&quot;
126 *         android:valueType=&quot;pathType&quot;/&gt;
127 * &lt;/set&gt;
128 * </pre></li>
129 *
130 * @attr ref android.R.styleable#AnimatedVectorDrawableCompat_drawable
131 * @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_name
132 * @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_animation
133 */
134@TargetApi(Build.VERSION_CODES.LOLLIPOP)
135public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable {
136    private static final String LOGTAG = "AnimatedVDCompat";
137
138    private static final String ANIMATED_VECTOR = "animated-vector";
139    private static final String TARGET = "target";
140
141    private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
142
143    private AnimatedVectorDrawableCompatState mAnimatedVectorState;
144
145    private Context mContext;
146
147    private ArgbEvaluator mArgbEvaluator = null;
148
149    AnimatedVectorDrawableDelegateState mCachedConstantStateDelegate;
150
151    private AnimatedVectorDrawableCompat() {
152        this(null, null, null);
153    }
154
155    private AnimatedVectorDrawableCompat(@Nullable Context context) {
156        this(context, null, null);
157    }
158
159    private AnimatedVectorDrawableCompat(@Nullable Context context,
160                                         @Nullable AnimatedVectorDrawableCompatState state,
161                                         @Nullable Resources res) {
162        mContext = context;
163        if (state != null) {
164            mAnimatedVectorState = state;
165        } else {
166            mAnimatedVectorState = new AnimatedVectorDrawableCompatState(context, state, mCallback,
167                    res);
168        }
169    }
170
171    @Override
172    public Drawable mutate() {
173        if (mDelegateDrawable != null) {
174            mDelegateDrawable.mutate();
175            return this;
176        }
177        throw new IllegalStateException("Mutate() is not supported for older platform");
178    }
179
180
181    /**
182     * Create a AnimatedVectorDrawableCompat object.
183     *
184     * @param context the context for creating the animators.
185     * @param resId   the resource ID for AnimatedVectorDrawableCompat object.
186     * @return a new AnimatedVectorDrawableCompat or null if parsing error is found.
187     */
188    @Nullable
189    public static AnimatedVectorDrawableCompat create(@NonNull Context context,
190                                                      @DrawableRes int resId) {
191        if (Build.VERSION.SDK_INT >= 21) {
192            final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
193            drawable.mDelegateDrawable = ResourcesCompat.getDrawable(context.getResources(), resId,
194                    context.getTheme());
195            drawable.mDelegateDrawable.setCallback(drawable.mCallback);
196            drawable.mCachedConstantStateDelegate = new AnimatedVectorDrawableDelegateState(
197                    drawable.mDelegateDrawable.getConstantState());
198            return drawable;
199        }
200        Resources resources = context.getResources();
201        try {
202            final XmlPullParser parser = resources.getXml(resId);
203            final AttributeSet attrs = Xml.asAttributeSet(parser);
204            int type;
205            while ((type = parser.next()) != XmlPullParser.START_TAG
206                    && type != XmlPullParser.END_DOCUMENT) {
207                // Empty loop
208            }
209            if (type != XmlPullParser.START_TAG) {
210                throw new XmlPullParserException("No start tag found");
211            }
212            return createFromXmlInner(context, context.getResources(), parser, attrs,
213                    context.getTheme());
214        } catch (XmlPullParserException e) {
215            Log.e(LOGTAG, "parser error", e);
216        } catch (IOException e) {
217            Log.e(LOGTAG, "parser error", e);
218        }
219        return null;
220    }
221
222    /**
223     * Create a AnimatedVectorDrawableCompat from inside an XML document using an optional
224     * {@link Theme}. Called on a parser positioned at a tag in an XML
225     * document, tries to create a Drawable from that tag. Returns {@code null}
226     * if the tag is not a valid drawable.
227     */
228    public static AnimatedVectorDrawableCompat createFromXmlInner(Context context, Resources r,
229            XmlPullParser parser, AttributeSet attrs, Theme theme)
230            throws XmlPullParserException, IOException {
231        final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
232        drawable.inflate(r, parser, attrs, theme);
233        return drawable;
234    }
235
236    /**
237     * {@inheritDoc}
238     * <strong>Note</strong> that we don't support constant state when SDK < 21.
239     * Make sure you check the return value before using it.
240     */
241    @Override
242    public ConstantState getConstantState() {
243        if (mDelegateDrawable != null) {
244            return new AnimatedVectorDrawableDelegateState(mDelegateDrawable.getConstantState());
245        }
246        // We can't support constant state in older platform.
247        // We need Context to create the animator, and we can't save the context in the constant
248        // state.
249        return null;
250    }
251
252    @Override
253    public int getChangingConfigurations() {
254        if (mDelegateDrawable != null) {
255            return mDelegateDrawable.getChangingConfigurations();
256        }
257        return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations;
258    }
259
260    @Override
261    public void draw(Canvas canvas) {
262        if (mDelegateDrawable != null) {
263            mDelegateDrawable.draw(canvas);
264            return;
265        }
266        mAnimatedVectorState.mVectorDrawable.draw(canvas);
267        if (isStarted()) {
268            invalidateSelf();
269        }
270    }
271
272    @Override
273    protected void onBoundsChange(Rect bounds) {
274        if (mDelegateDrawable != null) {
275            mDelegateDrawable.setBounds(bounds);
276            return;
277        }
278        mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
279    }
280
281    @Override
282    protected boolean onStateChange(int[] state) {
283        if (mDelegateDrawable != null) {
284            return mDelegateDrawable.setState(state);
285        }
286        return mAnimatedVectorState.mVectorDrawable.setState(state);
287    }
288
289    @Override
290    protected boolean onLevelChange(int level) {
291        if (mDelegateDrawable != null) {
292            return mDelegateDrawable.setLevel(level);
293        }
294        return mAnimatedVectorState.mVectorDrawable.setLevel(level);
295    }
296
297    @Override
298    public int getAlpha() {
299        if (mDelegateDrawable != null) {
300            return DrawableCompat.getAlpha(mDelegateDrawable);
301        }
302        return mAnimatedVectorState.mVectorDrawable.getAlpha();
303    }
304
305    @Override
306    public void setAlpha(int alpha) {
307        if (mDelegateDrawable != null) {
308            mDelegateDrawable.setAlpha(alpha);
309            return;
310        }
311        mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
312    }
313
314    @Override
315    public void setColorFilter(ColorFilter colorFilter) {
316        if (mDelegateDrawable != null) {
317            mDelegateDrawable.setColorFilter(colorFilter);
318            return;
319        }
320        mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
321    }
322
323    public void setTint(int tint) {
324        if (mDelegateDrawable != null) {
325            DrawableCompat.setTint(mDelegateDrawable, tint);
326            return;
327        }
328
329        mAnimatedVectorState.mVectorDrawable.setTint(tint);
330    }
331
332    public void setTintList(ColorStateList tint) {
333        if (mDelegateDrawable != null) {
334            DrawableCompat.setTintList(mDelegateDrawable, tint);
335            return;
336        }
337
338        mAnimatedVectorState.mVectorDrawable.setTintList(tint);
339    }
340
341    public void setTintMode(PorterDuff.Mode tintMode) {
342        if (mDelegateDrawable != null) {
343            DrawableCompat.setTintMode(mDelegateDrawable, tintMode);
344            return;
345        }
346
347        mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
348    }
349
350    @Override
351    public boolean setVisible(boolean visible, boolean restart) {
352        if (mDelegateDrawable != null) {
353            return mDelegateDrawable.setVisible(visible, restart);
354        }
355        mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
356        return super.setVisible(visible, restart);
357    }
358
359    @Override
360    public boolean isStateful() {
361        if (mDelegateDrawable != null) {
362            return mDelegateDrawable.isStateful();
363        }
364        return mAnimatedVectorState.mVectorDrawable.isStateful();
365    }
366
367    @Override
368    public int getOpacity() {
369        if (mDelegateDrawable != null) {
370            return mDelegateDrawable.getOpacity();
371        }
372        return mAnimatedVectorState.mVectorDrawable.getOpacity();
373    }
374
375    public int getIntrinsicWidth() {
376        if (mDelegateDrawable != null) {
377            return mDelegateDrawable.getIntrinsicWidth();
378        }
379        return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
380    }
381
382    public int getIntrinsicHeight() {
383        if (mDelegateDrawable != null) {
384            return mDelegateDrawable.getIntrinsicHeight();
385        }
386        return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
387    }
388
389    /**
390     * Obtains styled attributes from the theme, if available, or unstyled
391     * resources if the theme is null.
392     */
393    static TypedArray obtainAttributes(
394            Resources res, Theme theme, AttributeSet set, int[] attrs) {
395        if (theme == null) {
396            return res.obtainAttributes(set, attrs);
397        }
398        return theme.obtainStyledAttributes(set, attrs, 0, 0);
399    }
400
401    @Override
402    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
403            throws XmlPullParserException, IOException {
404        if (mDelegateDrawable != null) {
405            DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme);
406            return;
407        }
408        int eventType = parser.getEventType();
409        while (eventType != XmlPullParser.END_DOCUMENT) {
410            if (eventType == XmlPullParser.START_TAG) {
411                final String tagName = parser.getName();
412                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
413                    Log.v(LOGTAG, "tagName is " + tagName);
414                }
415                if (ANIMATED_VECTOR.equals(tagName)) {
416                    final TypedArray a =
417                            obtainAttributes(res, theme, attrs,
418                                    AndroidResources.styleable_AnimatedVectorDrawable);
419
420                    int drawableRes = a.getResourceId(
421                            AndroidResources.styleable_AnimatedVectorDrawable_drawable, 0);
422                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
423                        Log.v(LOGTAG, "drawableRes is " + drawableRes);
424                    }
425                    if (drawableRes != 0) {
426                        VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(res,
427                                drawableRes, theme);
428                        vectorDrawable.setAllowCaching(false);
429                        vectorDrawable.setCallback(mCallback);
430                        if (mAnimatedVectorState.mVectorDrawable != null) {
431                            mAnimatedVectorState.mVectorDrawable.setCallback(null);
432                        }
433                        mAnimatedVectorState.mVectorDrawable = vectorDrawable;
434                    }
435                    a.recycle();
436                } else if (TARGET.equals(tagName)) {
437                    final TypedArray a =
438                            res.obtainAttributes(attrs,
439                                    AndroidResources.styleable_AnimatedVectorDrawableTarget);
440                    final String target = a.getString(
441                            AndroidResources.styleable_AnimatedVectorDrawableTarget_name);
442
443                    int id = a.getResourceId(
444                            AndroidResources.styleable_AnimatedVectorDrawableTarget_animation, 0);
445                    if (id != 0) {
446                        if (mContext != null) {
447                            Animator objectAnimator = AnimatorInflater.loadAnimator(mContext, id);
448                            setupAnimatorsForTarget(target, objectAnimator);
449                        } else {
450                            throw new IllegalStateException("Context can't be null when inflating" +
451                                    " animators");
452                        }
453                    }
454                    a.recycle();
455                }
456            }
457
458            eventType = parser.next();
459        }
460    }
461
462    @Override
463    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
464            throws XmlPullParserException, IOException {
465        inflate(res, parser, attrs, null);
466    }
467
468    @Override
469    public void applyTheme(Theme t) {
470        if (mDelegateDrawable != null) {
471            DrawableCompat.applyTheme(mDelegateDrawable, t);
472            return;
473        }
474        // TODO: support theming in older platform.
475        return;
476    }
477
478    public boolean canApplyTheme() {
479        if (mDelegateDrawable != null) {
480            return DrawableCompat.canApplyTheme(mDelegateDrawable);
481        }
482        // TODO: support theming in older platform.
483        return false;
484    }
485
486    /**
487     * Constant state for delegating the creating drawable job.
488     * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
489     * a delegated VectorDrawable instance.
490     */
491    private static class AnimatedVectorDrawableDelegateState extends ConstantState {
492        private final ConstantState mDelegateState;
493
494        public AnimatedVectorDrawableDelegateState(ConstantState state) {
495            mDelegateState = state;
496        }
497
498        @Override
499        public Drawable newDrawable() {
500            AnimatedVectorDrawableCompat drawableCompat =
501                    new AnimatedVectorDrawableCompat();
502            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable();
503            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
504            return drawableCompat;
505        }
506
507        @Override
508        public Drawable newDrawable(Resources res) {
509            AnimatedVectorDrawableCompat drawableCompat =
510                    new AnimatedVectorDrawableCompat();
511            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res);
512            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
513            return drawableCompat;
514        }
515
516        @Override
517        public Drawable newDrawable(Resources res, Theme theme) {
518            AnimatedVectorDrawableCompat drawableCompat =
519                    new AnimatedVectorDrawableCompat();
520            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res, theme);
521            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
522            return drawableCompat;
523        }
524
525        @Override
526        public boolean canApplyTheme() {
527            return mDelegateState.canApplyTheme();
528        }
529
530        @Override
531        public int getChangingConfigurations() {
532            return mDelegateState.getChangingConfigurations();
533        }
534    }
535
536    private static class AnimatedVectorDrawableCompatState extends ConstantState {
537        int mChangingConfigurations;
538        VectorDrawableCompat mVectorDrawable;
539        ArrayList<Animator> mAnimators;
540        ArrayMap<Animator, String> mTargetNameMap;
541
542        public AnimatedVectorDrawableCompatState(Context context,
543                AnimatedVectorDrawableCompatState copy, Callback owner, Resources res) {
544            if (copy != null) {
545                mChangingConfigurations = copy.mChangingConfigurations;
546                if (copy.mVectorDrawable != null) {
547                    final ConstantState cs = copy.mVectorDrawable.getConstantState();
548                    if (res != null) {
549                        mVectorDrawable = (VectorDrawableCompat) cs.newDrawable(res);
550                    } else {
551                        mVectorDrawable = (VectorDrawableCompat) cs.newDrawable();
552                    }
553                    mVectorDrawable = (VectorDrawableCompat) mVectorDrawable.mutate();
554                    mVectorDrawable.setCallback(owner);
555                    mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
556                    mVectorDrawable.setAllowCaching(false);
557                }
558                if (copy.mAnimators != null) {
559                    final int numAnimators = copy.mAnimators.size();
560                    mAnimators = new ArrayList<Animator>(numAnimators);
561                    mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
562                    for (int i = 0; i < numAnimators; ++i) {
563                        Animator anim = copy.mAnimators.get(i);
564                        Animator animClone = anim.clone();
565                        String targetName = copy.mTargetNameMap.get(anim);
566                        Object targetObject = mVectorDrawable.getTargetByName(targetName);
567                        animClone.setTarget(targetObject);
568                        mAnimators.add(animClone);
569                        mTargetNameMap.put(animClone, targetName);
570                    }
571                }
572            }
573        }
574
575        @Override
576        public Drawable newDrawable() {
577            throw new IllegalStateException("No constant state support for SDK < 21.");
578        }
579
580        @Override
581        public Drawable newDrawable(Resources res) {
582            throw new IllegalStateException("No constant state support for SDK < 21.");
583        }
584
585        @Override
586        public int getChangingConfigurations() {
587            return mChangingConfigurations;
588        }
589    }
590
591    /**
592     * Utility function to fix color interpolation prior to Lollipop. Without this fix, colors
593     * are evaluated as raw integers instead of as colors, which leads to artifacts during
594     * fillColor animations.
595     */
596    private void setupColorAnimator(Animator animator) {
597        if (animator instanceof AnimatorSet) {
598            List<Animator> childAnimators = ((AnimatorSet) animator).getChildAnimations();
599            if (childAnimators != null) {
600                for (int i = 0; i < childAnimators.size(); ++i) {
601                    setupColorAnimator(childAnimators.get(i));
602                }
603            }
604        }
605        if (animator instanceof ObjectAnimator) {
606            ObjectAnimator objectAnim = (ObjectAnimator) animator;
607            final String propertyName = objectAnim.getPropertyName();
608            if ("fillColor".equals(propertyName) || "strokeColor".equals(propertyName)) {
609                if (mArgbEvaluator == null) {
610                    mArgbEvaluator = new ArgbEvaluator();
611                }
612                objectAnim.setEvaluator(mArgbEvaluator);
613            }
614        }
615    }
616
617    private void setupAnimatorsForTarget(String name, Animator animator) {
618        Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
619        animator.setTarget(target);
620        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
621            setupColorAnimator(animator);
622        }
623        if (mAnimatedVectorState.mAnimators == null) {
624            mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
625            mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
626        }
627        mAnimatedVectorState.mAnimators.add(animator);
628        mAnimatedVectorState.mTargetNameMap.put(animator, name);
629        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
630            Log.v(LOGTAG, "add animator  for target " + name + " " + animator);
631        }
632    }
633
634    @Override
635    public boolean isRunning() {
636        if (mDelegateDrawable != null) {
637            return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
638        }
639        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
640        final int size = animators.size();
641        for (int i = 0; i < size; i++) {
642            final Animator animator = animators.get(i);
643            if (animator.isRunning()) {
644                return true;
645            }
646        }
647        return false;
648    }
649
650    private boolean isStarted() {
651        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
652        if (animators == null) {
653            return false;
654        }
655        final int size = animators.size();
656        for (int i = 0; i < size; i++) {
657            final Animator animator = animators.get(i);
658            if (animator.isRunning()) {
659                return true;
660            }
661        }
662        return false;
663    }
664
665    @Override
666    public void start() {
667        if (mDelegateDrawable != null) {
668            ((AnimatedVectorDrawable) mDelegateDrawable).start();
669            return;
670        }
671        // If any one of the animator has not ended, do nothing.
672        if (isStarted()) {
673            return;
674        }
675        // Otherwise, kick off every animator.
676        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
677        final int size = animators.size();
678        for (int i = 0; i < size; i++) {
679            final Animator animator = animators.get(i);
680            animator.start();
681        }
682        invalidateSelf();
683    }
684
685    @Override
686    public void stop() {
687        if (mDelegateDrawable != null) {
688            ((AnimatedVectorDrawable) mDelegateDrawable).stop();
689            return;
690        }
691        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
692        final int size = animators.size();
693        for (int i = 0; i < size; i++) {
694            final Animator animator = animators.get(i);
695            animator.end();
696        }
697    }
698
699    private final Callback mCallback = new Callback() {
700        @Override
701        public void invalidateDrawable(Drawable who) {
702            invalidateSelf();
703        }
704
705        @Override
706        public void scheduleDrawable(Drawable who, Runnable what, long when) {
707            scheduleSelf(what, when);
708        }
709
710        @Override
711        public void unscheduleDrawable(Drawable who, Runnable what) {
712            unscheduleSelf(what);
713        }
714    };
715}
716