AnimatedVectorDrawable.java revision 18ad5f43685489e0086dce74f2bfcca9ba39daa3
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            localAnimator.setTarget(target);
838            return localAnimator;
839        }
840
841        /**
842         * Inflates pending animators, if any, against a theme. Clears the list of
843         * pending animators.
844         *
845         * @param t the theme against which to inflate the animators
846         */
847        public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) {
848            final ArrayList<PendingAnimator> pendingAnims = mPendingAnims;
849            if (pendingAnims != null) {
850                mPendingAnims = null;
851
852                for (int i = 0, count = pendingAnims.size(); i < count; i++) {
853                    final PendingAnimator pendingAnimator = pendingAnims.get(i);
854                    final Animator animator = pendingAnimator.newInstance(res, t);
855                    updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
856                            mShouldIgnoreInvalidAnim);
857                    addTargetAnimator(pendingAnimator.target, animator);
858                }
859            }
860        }
861
862        /**
863         * Basically a constant state for Animators until we actually implement
864         * constant states for Animators.
865         */
866        private static class PendingAnimator {
867            public final int animResId;
868            public final float pathErrorScale;
869            public final String target;
870
871            public PendingAnimator(int animResId, float pathErrorScale, String target) {
872                this.animResId = animResId;
873                this.pathErrorScale = pathErrorScale;
874                this.target = target;
875            }
876
877            public Animator newInstance(Resources res, Theme theme) {
878                return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
879            }
880        }
881    }
882
883    @Override
884    public boolean isRunning() {
885        return mAnimatorSet.isRunning();
886    }
887
888    /**
889     * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
890     */
891    public void reset() {
892        ensureAnimatorSet();
893        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
894            Log.w(LOGTAG, "calling reset on AVD: " +
895                    ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
896                    getConstantState()).mVectorDrawable.getConstantState()).mRootName
897                    + ", at: " + this);
898        }
899        mAnimatorSet.reset();
900    }
901
902    @Override
903    public void start() {
904        ensureAnimatorSet();
905        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
906            Log.w(LOGTAG, "calling start on AVD: " +
907                    ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
908                    getConstantState()).mVectorDrawable.getConstantState()).mRootName
909                    + ", at: " + this);
910        }
911        mAnimatorSet.start();
912    }
913
914    @NonNull
915    private void ensureAnimatorSet() {
916        if (mAnimatorSetFromXml == null) {
917            // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
918            // with a list of LocalAnimators.
919            mAnimatorSetFromXml = new AnimatorSet();
920            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSetFromXml, mRes);
921            mAnimatorSet.init(mAnimatorSetFromXml);
922            mRes = null;
923        }
924    }
925
926    @Override
927    public void stop() {
928        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
929            Log.w(LOGTAG, "calling stop on AVD: " +
930                    ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
931                            getConstantState()).mVectorDrawable.getConstantState())
932                            .mRootName + ", at: " + this);
933        }
934        mAnimatorSet.end();
935    }
936
937    /**
938     * Reverses ongoing animations or starts pending animations in reverse.
939     * <p>
940     * NOTE: Only works if all animations support reverse. Otherwise, this will
941     * do nothing.
942     * @hide
943     */
944    public void reverse() {
945        ensureAnimatorSet();
946
947        // Only reverse when all the animators can be reversed.
948        if (!canReverse()) {
949            Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
950            return;
951        }
952
953        mAnimatorSet.reverse();
954    }
955
956    /**
957     * @hide
958     */
959    public boolean canReverse() {
960        return mAnimatorSet.canReverse();
961    }
962
963    private final Callback mCallback = new Callback() {
964        @Override
965        public void invalidateDrawable(@NonNull Drawable who) {
966            invalidateSelf();
967        }
968
969        @Override
970        public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
971            scheduleSelf(what, when);
972        }
973
974        @Override
975        public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
976            unscheduleSelf(what);
977        }
978    };
979
980    @Override
981    public void registerAnimationCallback(@NonNull AnimationCallback callback) {
982        if (callback == null) {
983            return;
984        }
985
986        // Add listener accordingly.
987        if (mAnimationCallbacks == null) {
988            mAnimationCallbacks = new ArrayList<>();
989        }
990
991        mAnimationCallbacks.add(callback);
992
993        if (mAnimatorListener == null) {
994            // Create a animator listener and trigger the callback events when listener is
995            // triggered.
996            mAnimatorListener = new AnimatorListenerAdapter() {
997                @Override
998                public void onAnimationStart(Animator animation) {
999                    ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1000                    int size = tmpCallbacks.size();
1001                    for (int i = 0; i < size; i ++) {
1002                        tmpCallbacks.get(i).onAnimationStart(AnimatedVectorDrawable.this);
1003                    }
1004                }
1005
1006                @Override
1007                public void onAnimationEnd(Animator animation) {
1008                    ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
1009                    int size = tmpCallbacks.size();
1010                    for (int i = 0; i < size; i ++) {
1011                        tmpCallbacks.get(i).onAnimationEnd(AnimatedVectorDrawable.this);
1012                    }
1013                }
1014            };
1015        }
1016        mAnimatorSet.setListener(mAnimatorListener);
1017    }
1018
1019    // A helper function to clean up the animator listener in the mAnimatorSet.
1020    private void removeAnimatorSetListener() {
1021        if (mAnimatorListener != null) {
1022            mAnimatorSet.removeListener(mAnimatorListener);
1023            mAnimatorListener = null;
1024        }
1025    }
1026
1027    @Override
1028    public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
1029        if (mAnimationCallbacks == null || callback == null) {
1030            // Nothing to be removed.
1031            return false;
1032        }
1033        boolean removed = mAnimationCallbacks.remove(callback);
1034
1035        //  When the last call back unregistered, remove the listener accordingly.
1036        if (mAnimationCallbacks.size() == 0) {
1037            removeAnimatorSetListener();
1038        }
1039        return removed;
1040    }
1041
1042    @Override
1043    public void clearAnimationCallbacks() {
1044        removeAnimatorSetListener();
1045        if (mAnimationCallbacks == null) {
1046            return;
1047        }
1048
1049        mAnimationCallbacks.clear();
1050    }
1051
1052    private interface VectorDrawableAnimator {
1053        void init(@NonNull AnimatorSet set);
1054        void start();
1055        void end();
1056        void reset();
1057        void reverse();
1058        boolean canReverse();
1059        void setListener(AnimatorListener listener);
1060        void removeListener(AnimatorListener listener);
1061        void onDraw(Canvas canvas);
1062        boolean isStarted();
1063        boolean isRunning();
1064        boolean isInfinite();
1065        void pause();
1066        void resume();
1067    }
1068
1069    private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
1070        // mSet is only initialized in init(). So we need to check whether it is null before any
1071        // operation.
1072        private AnimatorSet mSet = null;
1073        private final Drawable mDrawable;
1074        // Caching the listener in the case when listener operation is called before the mSet is
1075        // setup by init().
1076        private ArrayList<AnimatorListener> mListenerArray = null;
1077        private boolean mIsInfinite = false;
1078
1079        VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
1080            mDrawable = drawable;
1081        }
1082
1083        @Override
1084        public void init(@NonNull AnimatorSet set) {
1085            if (mSet != null) {
1086                // Already initialized
1087                throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1088                        "re-initialized");
1089            }
1090            // Keep a deep copy of the set, such that set can be still be constantly representing
1091            // the static content from XML file.
1092            mSet = set.clone();
1093            mIsInfinite = mSet.getTotalDuration() == Animator.DURATION_INFINITE;
1094
1095            // If there are listeners added before calling init(), now they should be setup.
1096            if (mListenerArray != null && !mListenerArray.isEmpty()) {
1097                for (int i = 0; i < mListenerArray.size(); i++) {
1098                    mSet.addListener(mListenerArray.get(i));
1099                }
1100                mListenerArray.clear();
1101                mListenerArray = null;
1102            }
1103        }
1104
1105        // Although start(), reset() and reverse() should call init() already, it is better to
1106        // protect these functions from NPE in any situation.
1107        @Override
1108        public void start() {
1109            if (mSet == null || mSet.isStarted()) {
1110                return;
1111            }
1112            mSet.start();
1113            invalidateOwningView();
1114        }
1115
1116        @Override
1117        public void end() {
1118            if (mSet == null) {
1119                return;
1120            }
1121            mSet.end();
1122        }
1123
1124        @Override
1125        public void reset() {
1126            if (mSet == null) {
1127                return;
1128            }
1129            start();
1130            mSet.cancel();
1131        }
1132
1133        @Override
1134        public void reverse() {
1135            if (mSet == null) {
1136                return;
1137            }
1138            mSet.reverse();
1139            invalidateOwningView();
1140        }
1141
1142        @Override
1143        public boolean canReverse() {
1144            return mSet != null && mSet.canReverse();
1145        }
1146
1147        @Override
1148        public void setListener(AnimatorListener listener) {
1149            if (mSet == null) {
1150                if (mListenerArray == null) {
1151                    mListenerArray = new ArrayList<AnimatorListener>();
1152                }
1153                mListenerArray.add(listener);
1154            } else {
1155                mSet.addListener(listener);
1156            }
1157        }
1158
1159        @Override
1160        public void removeListener(AnimatorListener listener) {
1161            if (mSet == null) {
1162                if (mListenerArray == null) {
1163                    return;
1164                }
1165                mListenerArray.remove(listener);
1166            } else {
1167                mSet.removeListener(listener);
1168            }
1169        }
1170
1171        @Override
1172        public void onDraw(Canvas canvas) {
1173            if (mSet != null && mSet.isStarted()) {
1174                invalidateOwningView();
1175            }
1176        }
1177
1178        @Override
1179        public boolean isStarted() {
1180            return mSet != null && mSet.isStarted();
1181        }
1182
1183        @Override
1184        public boolean isRunning() {
1185            return mSet != null && mSet.isRunning();
1186        }
1187
1188        @Override
1189        public boolean isInfinite() {
1190            return mIsInfinite;
1191        }
1192
1193        @Override
1194        public void pause() {
1195            if (mSet == null) {
1196                return;
1197            }
1198            mSet.pause();
1199        }
1200
1201        @Override
1202        public void resume() {
1203            if (mSet == null) {
1204                return;
1205            }
1206            mSet.resume();
1207        }
1208
1209        private void invalidateOwningView() {
1210            mDrawable.invalidateSelf();
1211        }
1212    }
1213
1214    /**
1215     * @hide
1216     */
1217    public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator {
1218        private static final int START_ANIMATION = 1;
1219        private static final int REVERSE_ANIMATION = 2;
1220        private static final int RESET_ANIMATION = 3;
1221        private static final int END_ANIMATION = 4;
1222
1223        // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
1224        private static final int MAX_SAMPLE_POINTS = 300;
1225        private AnimatorListener mListener = null;
1226        private final LongArray mStartDelays = new LongArray();
1227        private PropertyValuesHolder.PropertyValues mTmpValues =
1228                new PropertyValuesHolder.PropertyValues();
1229        private long mSetPtr = 0;
1230        private boolean mContainsSequentialAnimators = false;
1231        private boolean mStarted = false;
1232        private boolean mInitialized = false;
1233        private boolean mIsReversible = false;
1234        private boolean mIsInfinite = false;
1235        // TODO: Consider using NativeAllocationRegistery to track native allocation
1236        private final VirtualRefBasePtr mSetRefBasePtr;
1237        private WeakReference<RenderNode> mLastSeenTarget = null;
1238        private int mLastListenerId = 0;
1239        private final IntArray mPendingAnimationActions = new IntArray();
1240        private final AnimatedVectorDrawable mDrawable;
1241
1242        VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
1243            mDrawable = drawable;
1244            mSetPtr = nCreateAnimatorSet();
1245            // Increment ref count on native AnimatorSet, so it doesn't get released before Java
1246            // side is done using it.
1247            mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
1248        }
1249
1250        @Override
1251        public void init(@NonNull AnimatorSet set) {
1252            if (mInitialized) {
1253                // Already initialized
1254                throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
1255                        "re-initialized");
1256            }
1257            parseAnimatorSet(set, 0);
1258            long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
1259                    .getNativeTree();
1260            nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
1261            mInitialized = true;
1262            mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
1263
1264            // Check reversible.
1265            mIsReversible = true;
1266            if (mContainsSequentialAnimators) {
1267                mIsReversible = false;
1268            } else {
1269                // Check if there's any start delay set on child
1270                for (int i = 0; i < mStartDelays.size(); i++) {
1271                    if (mStartDelays.get(i) > 0) {
1272                        mIsReversible = false;
1273                        return;
1274                    }
1275                }
1276            }
1277        }
1278
1279        private void parseAnimatorSet(AnimatorSet set, long startTime) {
1280            ArrayList<Animator> animators = set.getChildAnimations();
1281
1282            boolean playTogether = set.shouldPlayTogether();
1283            // Convert AnimatorSet to VectorDrawableAnimatorRT
1284            for (int i = 0; i < animators.size(); i++) {
1285                Animator animator = animators.get(i);
1286                // Here we only support ObjectAnimator
1287                if (animator instanceof AnimatorSet) {
1288                    parseAnimatorSet((AnimatorSet) animator, startTime);
1289                } else if (animator instanceof ObjectAnimator) {
1290                    createRTAnimator((ObjectAnimator) animator, startTime);
1291                } // ignore ValueAnimators and others because they don't directly modify VD
1292                  // therefore will be useless to AVD.
1293
1294                if (!playTogether) {
1295                    // Assume not play together means play sequentially
1296                    startTime += animator.getTotalDuration();
1297                    mContainsSequentialAnimators = true;
1298                }
1299            }
1300        }
1301
1302        // TODO: This method reads animation data from already parsed Animators. We need to move
1303        // this step further up the chain in the parser to avoid the detour.
1304        private void createRTAnimator(ObjectAnimator animator, long startTime) {
1305            PropertyValuesHolder[] values = animator.getValues();
1306            Object target = animator.getTarget();
1307            if (target instanceof VectorDrawable.VGroup) {
1308                createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
1309                        startTime);
1310            } else if (target instanceof VectorDrawable.VPath) {
1311                for (int i = 0; i < values.length; i++) {
1312                    values[i].getPropertyValues(mTmpValues);
1313                    if (mTmpValues.endValue instanceof PathParser.PathData &&
1314                            mTmpValues.propertyName.equals("pathData")) {
1315                        createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
1316                                startTime);
1317                    }  else if (target instanceof VectorDrawable.VFullPath) {
1318                        createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
1319                                startTime);
1320                    } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1321                        throw new IllegalArgumentException("ClipPath only supports PathData " +
1322                                "property");
1323                    }
1324
1325                }
1326            } else if (target instanceof VectorDrawable.VectorDrawableState) {
1327                createRTAnimatorForRootGroup(values, animator,
1328                        (VectorDrawable.VectorDrawableState) target, startTime);
1329            } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1330                // Should never get here
1331                throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
1332                        "or ConstantState, " + target == null ? "Null target" : target.getClass() +
1333                        " is not supported");
1334            }
1335        }
1336
1337        private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
1338                ObjectAnimator animator, VectorDrawable.VGroup target,
1339                long startTime) {
1340
1341            long nativePtr = target.getNativePtr();
1342            int propertyId;
1343            for (int i = 0; i < values.length; i++) {
1344                // TODO: We need to support the rare case in AVD where no start value is provided
1345                values[i].getPropertyValues(mTmpValues);
1346                propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
1347                if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
1348                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1349                        Log.e(LOGTAG, "Unsupported type: " +
1350                                mTmpValues.type + ". Only float value is supported for Groups.");
1351                    }
1352                    continue;
1353                }
1354                if (propertyId < 0) {
1355                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1356                        Log.e(LOGTAG, "Unsupported property: " +
1357                                mTmpValues.propertyName + " for Vector Drawable Group");
1358                    }
1359                    continue;
1360                }
1361                long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
1362                        (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1363                if (mTmpValues.dataSource != null) {
1364                    float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1365                            animator.getDuration());
1366                    nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1367                }
1368                createNativeChildAnimator(propertyPtr, startTime, animator);
1369            }
1370        }
1371        private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
1372                long startTime) {
1373
1374            long nativePtr = target.getNativePtr();
1375            long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
1376                    .getNativePtr();
1377            long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
1378                    .getNativePtr();
1379            long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
1380                    endPathDataPtr);
1381            createNativeChildAnimator(propertyPtr, startTime, animator);
1382        }
1383
1384        private void createRTAnimatorForFullPath(ObjectAnimator animator,
1385                VectorDrawable.VFullPath target, long startTime) {
1386
1387            int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
1388            long propertyPtr;
1389            long nativePtr = target.getNativePtr();
1390            if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
1391                if (propertyId < 0) {
1392                    if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1393                        return;
1394                    } else {
1395                        throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
1396                                + " is not supported for FullPath");
1397                    }
1398                }
1399                propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
1400                        (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
1401                if (mTmpValues.dataSource != null) {
1402                    // Pass keyframe data to native, if any.
1403                    float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1404                            animator.getDuration());
1405                    nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1406                }
1407
1408            } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
1409                propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
1410                        (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
1411                if (mTmpValues.dataSource != null) {
1412                    // Pass keyframe data to native, if any.
1413                    int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
1414                            animator.getDuration());
1415                    nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1416                }
1417            } else {
1418                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1419                    return;
1420                } else {
1421                    throw new UnsupportedOperationException("Unsupported type: " +
1422                            mTmpValues.type + ". Only float, int or PathData value is " +
1423                            "supported for Paths.");
1424                }
1425            }
1426            createNativeChildAnimator(propertyPtr, startTime, animator);
1427        }
1428
1429        private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
1430                ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
1431                long startTime) {
1432            long nativePtr = target.getNativeRenderer();
1433            if (!animator.getPropertyName().equals("alpha")) {
1434                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1435                    return;
1436                } else {
1437                    throw new UnsupportedOperationException("Only alpha is supported for root "
1438                            + "group");
1439                }
1440            }
1441            Float startValue = null;
1442            Float endValue = null;
1443            for (int i = 0; i < values.length; i++) {
1444                values[i].getPropertyValues(mTmpValues);
1445                if (mTmpValues.propertyName.equals("alpha")) {
1446                    startValue = (Float) mTmpValues.startValue;
1447                    endValue = (Float) mTmpValues.endValue;
1448                    break;
1449                }
1450            }
1451            if (startValue == null && endValue == null) {
1452                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
1453                    return;
1454                } else {
1455                    throw new UnsupportedOperationException("No alpha values are specified");
1456                }
1457            }
1458            long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
1459            if (mTmpValues.dataSource != null) {
1460                // Pass keyframe data to native, if any.
1461                float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
1462                        animator.getDuration());
1463                nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
1464            }
1465            createNativeChildAnimator(propertyPtr, startTime, animator);
1466        }
1467
1468        /**
1469         * Calculate the amount of frames an animation will run based on duration.
1470         */
1471        private static int getFrameCount(long duration) {
1472            long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
1473            int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
1474            int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
1475            // We need 2 frames of data minimum.
1476            numAnimFrames = Math.max(2, numAnimFrames);
1477            if (numAnimFrames > MAX_SAMPLE_POINTS) {
1478                Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
1479                        duration + ", the animation will subsample the keyframe or path data.");
1480                numAnimFrames = MAX_SAMPLE_POINTS;
1481            }
1482            return numAnimFrames;
1483        }
1484
1485        // These are the data points that define the value of the animating properties.
1486        // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
1487        // a point on the path corresponds to the values of translateX and translateY.
1488        // TODO: (Optimization) We should pass the path down in native and chop it into segments
1489        // in native.
1490        private static float[] createFloatDataPoints(
1491                PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1492            int numAnimFrames = getFrameCount(duration);
1493            float values[] = new float[numAnimFrames];
1494            float lastFrame = numAnimFrames - 1;
1495            for (int i = 0; i < numAnimFrames; i++) {
1496                float fraction = i / lastFrame;
1497                values[i] = (Float) dataSource.getValueAtFraction(fraction);
1498            }
1499            return values;
1500        }
1501
1502        private static int[] createIntDataPoints(
1503                PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
1504            int numAnimFrames = getFrameCount(duration);
1505            int values[] = new int[numAnimFrames];
1506            float lastFrame = numAnimFrames - 1;
1507            for (int i = 0; i < numAnimFrames; i++) {
1508                float fraction = i / lastFrame;
1509                values[i] = (Integer) dataSource.getValueAtFraction(fraction);
1510            }
1511            return values;
1512        }
1513
1514        private void createNativeChildAnimator(long propertyPtr, long extraDelay,
1515                                               ObjectAnimator animator) {
1516            long duration = animator.getDuration();
1517            int repeatCount = animator.getRepeatCount();
1518            long startDelay = extraDelay + animator.getStartDelay();
1519            TimeInterpolator interpolator = animator.getInterpolator();
1520            long nativeInterpolator =
1521                    RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
1522
1523            startDelay *= ValueAnimator.getDurationScale();
1524            duration *= ValueAnimator.getDurationScale();
1525
1526            mStartDelays.add(startDelay);
1527            nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
1528                    repeatCount, animator.getRepeatMode());
1529        }
1530
1531        /**
1532         * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
1533         * in the last draw call), so that when animator set needs to start, we can add the animator
1534         * to the last seen RenderNode target and start right away.
1535         */
1536        protected void recordLastSeenTarget(DisplayListCanvas canvas) {
1537            final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
1538            mLastSeenTarget = new WeakReference<RenderNode>(node);
1539            // Add the animator to the list of animators on every draw
1540            if (mInitialized || mPendingAnimationActions.size() > 0) {
1541                if (useTarget(node)) {
1542                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1543                        Log.d(LOGTAG, "Target is set in the next frame");
1544                    }
1545                    for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1546                        handlePendingAction(mPendingAnimationActions.get(i));
1547                    }
1548                    mPendingAnimationActions.clear();
1549                }
1550            }
1551        }
1552
1553        private void handlePendingAction(int pendingAnimationAction) {
1554            if (pendingAnimationAction == START_ANIMATION) {
1555                startAnimation();
1556            } else if (pendingAnimationAction == REVERSE_ANIMATION) {
1557                reverseAnimation();
1558            } else if (pendingAnimationAction == RESET_ANIMATION) {
1559                resetAnimation();
1560            } else if (pendingAnimationAction == END_ANIMATION) {
1561                endAnimation();
1562            } else {
1563                throw new UnsupportedOperationException("Animation action " +
1564                        pendingAnimationAction + "is not supported");
1565            }
1566        }
1567
1568        private boolean useLastSeenTarget() {
1569            if (mLastSeenTarget != null) {
1570                final RenderNode target = mLastSeenTarget.get();
1571                return useTarget(target);
1572            }
1573            return false;
1574        }
1575
1576        private boolean useTarget(RenderNode target) {
1577            if (target != null && target.isAttached()) {
1578                target.registerVectorDrawableAnimator(this);
1579                return true;
1580            }
1581            return false;
1582        }
1583
1584        private void invalidateOwningView() {
1585            mDrawable.invalidateSelf();
1586        }
1587
1588        private void addPendingAction(int pendingAnimationAction) {
1589            invalidateOwningView();
1590            mPendingAnimationActions.add(pendingAnimationAction);
1591        }
1592
1593        @Override
1594        public void start() {
1595            if (!mInitialized) {
1596                return;
1597            }
1598
1599            if (useLastSeenTarget()) {
1600                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1601                    Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
1602                }
1603                startAnimation();
1604            } else {
1605                addPendingAction(START_ANIMATION);
1606            }
1607        }
1608
1609        @Override
1610        public void end() {
1611            if (!mInitialized) {
1612                return;
1613            }
1614
1615            if (useLastSeenTarget()) {
1616                endAnimation();
1617            } else {
1618                addPendingAction(END_ANIMATION);
1619            }
1620        }
1621
1622        @Override
1623        public void reset() {
1624            if (!mInitialized) {
1625                return;
1626            }
1627
1628            if (useLastSeenTarget()) {
1629                resetAnimation();
1630            } else {
1631                addPendingAction(RESET_ANIMATION);
1632            }
1633        }
1634
1635        // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
1636        // animators or when the animator set has a start delay
1637        @Override
1638        public void reverse() {
1639            if (!mIsReversible || !mInitialized) {
1640                return;
1641            }
1642            if (useLastSeenTarget()) {
1643                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1644                    Log.d(LOGTAG, "Target is set. Reversing VDAnimatorSet from java");
1645                }
1646                reverseAnimation();
1647            } else {
1648                addPendingAction(REVERSE_ANIMATION);
1649            }
1650        }
1651
1652        // This should only be called after animator has been added to the RenderNode target.
1653        private void startAnimation() {
1654            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1655                Log.w(LOGTAG, "starting animation on VD: " +
1656                        ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1657                                mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1658                                .mRootName);
1659            }
1660            mStarted = true;
1661            nStart(mSetPtr, this, ++mLastListenerId);
1662            invalidateOwningView();
1663            if (mListener != null) {
1664                mListener.onAnimationStart(null);
1665            }
1666        }
1667
1668        // This should only be called after animator has been added to the RenderNode target.
1669        private void endAnimation() {
1670            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1671                Log.w(LOGTAG, "ending animation on VD: " +
1672                        ((VectorDrawable.VectorDrawableState) ((AnimatedVectorDrawableState)
1673                                mDrawable.getConstantState()).mVectorDrawable.getConstantState())
1674                                .mRootName);
1675            }
1676            nEnd(mSetPtr);
1677            invalidateOwningView();
1678        }
1679
1680        // This should only be called after animator has been added to the RenderNode target.
1681        private void resetAnimation() {
1682            nReset(mSetPtr);
1683            invalidateOwningView();
1684        }
1685
1686        // This should only be called after animator has been added to the RenderNode target.
1687        private void reverseAnimation() {
1688            mStarted = true;
1689            nReverse(mSetPtr, this, ++mLastListenerId);
1690            invalidateOwningView();
1691            if (mListener != null) {
1692                mListener.onAnimationStart(null);
1693            }
1694        }
1695
1696        public long getAnimatorNativePtr() {
1697            return mSetPtr;
1698        }
1699
1700        @Override
1701        public boolean canReverse() {
1702            return mIsReversible;
1703        }
1704
1705        @Override
1706        public boolean isStarted() {
1707            return mStarted;
1708        }
1709
1710        @Override
1711        public boolean isRunning() {
1712            if (!mInitialized) {
1713                return false;
1714            }
1715            return mStarted;
1716        }
1717
1718        @Override
1719        public void setListener(AnimatorListener listener) {
1720            mListener = listener;
1721        }
1722
1723        @Override
1724        public void removeListener(AnimatorListener listener) {
1725            mListener = null;
1726        }
1727
1728        @Override
1729        public void onDraw(Canvas canvas) {
1730            if (canvas.isHardwareAccelerated()) {
1731                recordLastSeenTarget((DisplayListCanvas) canvas);
1732            }
1733        }
1734
1735        @Override
1736        public boolean isInfinite() {
1737            return mIsInfinite;
1738        }
1739
1740        @Override
1741        public void pause() {
1742            // TODO: Implement pause for Animator On RT.
1743        }
1744
1745        @Override
1746        public void resume() {
1747            // TODO: Implement resume for Animator On RT.
1748        }
1749
1750        private void onAnimationEnd(int listenerId) {
1751            if (listenerId != mLastListenerId) {
1752                return;
1753            }
1754            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1755                Log.d(LOGTAG, "on finished called from native");
1756            }
1757            mStarted = false;
1758            // Invalidate in the end of the animation to make sure the data in
1759            // RT thread is synced back to UI thread.
1760            invalidateOwningView();
1761            if (mListener != null) {
1762                mListener.onAnimationEnd(null);
1763            }
1764        }
1765
1766        // onFinished: should be called from native
1767        private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
1768            set.onAnimationEnd(id);
1769        }
1770
1771        private void transferPendingActions(VectorDrawableAnimator animatorSet) {
1772            for (int i = 0; i < mPendingAnimationActions.size(); i++) {
1773                int pendingAction = mPendingAnimationActions.get(i);
1774                if (pendingAction == START_ANIMATION) {
1775                    animatorSet.start();
1776                } else if (pendingAction == END_ANIMATION) {
1777                    animatorSet.end();
1778                } else if (pendingAction == REVERSE_ANIMATION) {
1779                    animatorSet.reverse();
1780                } else if (pendingAction == RESET_ANIMATION) {
1781                    animatorSet.reset();
1782                } else {
1783                    throw new UnsupportedOperationException("Animation action " +
1784                            pendingAction + "is not supported");
1785                }
1786            }
1787            mPendingAnimationActions.clear();
1788        }
1789    }
1790
1791    private static native long nCreateAnimatorSet();
1792    private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
1793    private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
1794            long nativeInterpolator, long startDelay, long duration, int repeatCount,
1795            int repeatMode);
1796    private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
1797    private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
1798    private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
1799    private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
1800
1801    // ------------- @FastNative -------------------
1802
1803    @FastNative
1804    private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
1805            float startValue, float endValue);
1806    @FastNative
1807    private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
1808            long endValuePtr);
1809    @FastNative
1810    private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
1811            int startValue, int endValue);
1812    @FastNative
1813    private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
1814            float startValue, float endValue);
1815    @FastNative
1816    private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
1817            float endValue);
1818    @FastNative
1819    private static native void nEnd(long animatorSetPtr);
1820    @FastNative
1821    private static native void nReset(long animatorSetPtr);
1822}
1823