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