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