OSDN Git Service

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