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