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