OSDN Git Service

0a3e27e94c064ef35a760ca73e808133f4c51813
[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.content.res.ColorStateList;
29 import android.content.res.Resources;
30 import android.content.res.Resources.Theme;
31 import android.content.res.TypedArray;
32 import android.graphics.Canvas;
33 import android.graphics.ColorFilter;
34 import android.graphics.Insets;
35 import android.graphics.Outline;
36 import android.graphics.PorterDuff;
37 import android.graphics.Rect;
38 import android.util.ArrayMap;
39 import android.util.AttributeSet;
40 import android.util.Log;
41 import android.util.LongArray;
42 import android.util.PathParser;
43 import android.util.TimeUtils;
44 import android.view.Choreographer;
45 import android.view.DisplayListCanvas;
46 import android.view.RenderNode;
47 import android.view.RenderNodeAnimatorSetHelper;
48 import android.view.View;
49
50 import com.android.internal.R;
51
52 import com.android.internal.util.VirtualRefBasePtr;
53 import org.xmlpull.v1.XmlPullParser;
54 import org.xmlpull.v1.XmlPullParserException;
55
56 import java.io.IOException;
57 import java.lang.ref.WeakReference;
58 import java.util.ArrayList;
59
60 /**
61  * This class uses {@link android.animation.ObjectAnimator} and
62  * {@link android.animation.AnimatorSet} to animate the properties of a
63  * {@link android.graphics.drawable.VectorDrawable} to create an animated drawable.
64  * <p>
65  * AnimatedVectorDrawable are normally defined as 3 separate XML files.
66  * </p>
67  * <p>
68  * First is the XML file for {@link android.graphics.drawable.VectorDrawable}.
69  * Note that we allow the animation to happen on the group's attributes and path's
70  * attributes, which requires they are uniquely named in this XML file. Groups
71  * and paths without animations do not need names.
72  * </p>
73  * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
74  * <pre>
75  * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
76  *     android:height=&quot;64dp&quot;
77  *     android:width=&quot;64dp&quot;
78  *     android:viewportHeight=&quot;600&quot;
79  *     android:viewportWidth=&quot;600&quot; &gt;
80  *     &lt;group
81  *         android:name=&quot;rotationGroup&quot;
82  *         android:pivotX=&quot;300.0&quot;
83  *         android:pivotY=&quot;300.0&quot;
84  *         android:rotation=&quot;45.0&quot; &gt;
85  *         &lt;path
86  *             android:name=&quot;v&quot;
87  *             android:fillColor=&quot;#000000&quot;
88  *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
89  *     &lt;/group&gt;
90  * &lt;/vector&gt;
91  * </pre></li>
92  * <p>
93  * Second is the AnimatedVectorDrawable's XML file, which defines the target
94  * VectorDrawable, the target paths and groups to animate, the properties of the
95  * path and group to animate and the animations defined as the ObjectAnimators
96  * or AnimatorSets.
97  * </p>
98  * <li>Here is a simple AnimatedVectorDrawable defined in this avd.xml file.
99  * Note how we use the names to refer to the groups and paths in the vectordrawable.xml.
100  * <pre>
101  * &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
102  *   android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
103  *     &lt;target
104  *         android:name=&quot;rotationGroup&quot;
105  *         android:animation=&quot;@anim/rotation&quot; /&gt;
106  *     &lt;target
107  *         android:name=&quot;v&quot;
108  *         android:animation=&quot;@anim/path_morph&quot; /&gt;
109  * &lt;/animated-vector&gt;
110  * </pre></li>
111  * <p>
112  * Last is the Animator XML file, which is the same as a normal ObjectAnimator
113  * or AnimatorSet.
114  * To complete this example, here are the 2 animator files used in avd.xml:
115  * rotation.xml and path_morph.xml.
116  * </p>
117  * <li>Here is the rotation.xml, which will rotate the target group for 360 degrees.
118  * <pre>
119  * &lt;objectAnimator
120  *     android:duration=&quot;6000&quot;
121  *     android:propertyName=&quot;rotation&quot;
122  *     android:valueFrom=&quot;0&quot;
123  *     android:valueTo=&quot;360&quot; /&gt;
124  * </pre></li>
125  * <li>Here is the path_morph.xml, which will morph the path from one shape to
126  * the other. Note that the paths must be compatible for morphing.
127  * In more details, the paths should have exact same length of commands , and
128  * exact same length of parameters for each commands.
129  * Note that the path strings are better stored in strings.xml for reusing.
130  * <pre>
131  * &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
132  *     &lt;objectAnimator
133  *         android:duration=&quot;3000&quot;
134  *         android:propertyName=&quot;pathData&quot;
135  *         android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0   -70,70z&quot;
136  *         android:valueTo=&quot;M300,70 l 0,-70 70,0  0,140 -70,0 z&quot;
137  *         android:valueType=&quot;pathType&quot;/&gt;
138  * &lt;/set&gt;
139  * </pre></li>
140  *
141  * @attr ref android.R.styleable#AnimatedVectorDrawable_drawable
142  * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name
143  * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_animation
144  */
145 public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
146     private static final String LOGTAG = "AnimatedVectorDrawable";
147
148     private static final String ANIMATED_VECTOR = "animated-vector";
149     private static final String TARGET = "target";
150
151     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
152
153     /** Local, mutable animator set. */
154     private final VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimator();
155
156     /**
157      * The resources against which this drawable was created. Used to attempt
158      * to inflate animators if applyTheme() doesn't get called.
159      */
160     private Resources mRes;
161
162     private AnimatedVectorDrawableState mAnimatedVectorState;
163
164     /** Whether the animator set has been prepared. */
165     private boolean mHasAnimatorSet;
166
167     private boolean mMutated;
168
169     /** Use a internal AnimatorListener to support callbacks during animation events. */
170     private ArrayList<Animatable2.AnimationCallback> mAnimationCallbacks = null;
171     private AnimatorListener mAnimatorListener = null;
172
173     public AnimatedVectorDrawable() {
174         this(null, null);
175     }
176
177     private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) {
178         mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
179         mRes = res;
180     }
181
182     @Override
183     public Drawable mutate() {
184         if (!mMutated && super.mutate() == this) {
185             mAnimatedVectorState = new AnimatedVectorDrawableState(
186                     mAnimatedVectorState, mCallback, mRes);
187             mMutated = true;
188         }
189         return this;
190     }
191
192     /**
193      * @hide
194      */
195     public void clearMutated() {
196         super.clearMutated();
197         if (mAnimatedVectorState.mVectorDrawable != null) {
198             mAnimatedVectorState.mVectorDrawable.clearMutated();
199         }
200         mMutated = false;
201     }
202
203     @Override
204     public ConstantState getConstantState() {
205         mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
206         return mAnimatedVectorState;
207     }
208
209     @Override
210     public int getChangingConfigurations() {
211         return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations();
212     }
213
214     @Override
215     public void draw(Canvas canvas) {
216         if (canvas.isHardwareAccelerated()) {
217             mAnimatorSet.recordLastSeenTarget((DisplayListCanvas) canvas);
218         }
219         mAnimatedVectorState.mVectorDrawable.draw(canvas);
220         if (isStarted()) {
221             invalidateSelf();
222         }
223     }
224
225     @Override
226     protected void onBoundsChange(Rect bounds) {
227         mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
228     }
229
230     @Override
231     protected boolean onStateChange(int[] state) {
232         return mAnimatedVectorState.mVectorDrawable.setState(state);
233     }
234
235     @Override
236     protected boolean onLevelChange(int level) {
237         return mAnimatedVectorState.mVectorDrawable.setLevel(level);
238     }
239
240     @Override
241     public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
242         return mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
243     }
244
245     @Override
246     public int getAlpha() {
247         return mAnimatedVectorState.mVectorDrawable.getAlpha();
248     }
249
250     @Override
251     public void setAlpha(int alpha) {
252         mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
253     }
254
255     @Override
256     public void setColorFilter(ColorFilter colorFilter) {
257         mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
258     }
259
260     @Override
261     public void setTintList(ColorStateList tint) {
262         mAnimatedVectorState.mVectorDrawable.setTintList(tint);
263     }
264
265     @Override
266     public void setHotspot(float x, float y) {
267         mAnimatedVectorState.mVectorDrawable.setHotspot(x, y);
268     }
269
270     @Override
271     public void setHotspotBounds(int left, int top, int right, int bottom) {
272         mAnimatedVectorState.mVectorDrawable.setHotspotBounds(left, top, right, bottom);
273     }
274
275     @Override
276     public void setTintMode(PorterDuff.Mode tintMode) {
277         mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
278     }
279
280     @Override
281     public boolean setVisible(boolean visible, boolean restart) {
282         mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
283         return super.setVisible(visible, restart);
284     }
285
286     @Override
287     public boolean isStateful() {
288         return mAnimatedVectorState.mVectorDrawable.isStateful();
289     }
290
291     @Override
292     public int getOpacity() {
293         return mAnimatedVectorState.mVectorDrawable.getOpacity();
294     }
295
296     @Override
297     public int getIntrinsicWidth() {
298         return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
299     }
300
301     @Override
302     public int getIntrinsicHeight() {
303         return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
304     }
305
306     @Override
307     public void getOutline(@NonNull Outline outline) {
308         mAnimatedVectorState.mVectorDrawable.getOutline(outline);
309     }
310
311     /** @hide */
312     @Override
313     public Insets getOpticalInsets() {
314         return mAnimatedVectorState.mVectorDrawable.getOpticalInsets();
315     }
316
317     @Override
318     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
319             throws XmlPullParserException, IOException {
320         final AnimatedVectorDrawableState state = mAnimatedVectorState;
321
322         int eventType = parser.getEventType();
323         float pathErrorScale = 1;
324         while (eventType != XmlPullParser.END_DOCUMENT) {
325             if (eventType == XmlPullParser.START_TAG) {
326                 final String tagName = parser.getName();
327                 if (ANIMATED_VECTOR.equals(tagName)) {
328                     final TypedArray a = obtainAttributes(res, theme, attrs,
329                             R.styleable.AnimatedVectorDrawable);
330                     int drawableRes = a.getResourceId(
331                             R.styleable.AnimatedVectorDrawable_drawable, 0);
332                     if (drawableRes != 0) {
333                         VectorDrawable vectorDrawable = (VectorDrawable) res.getDrawable(
334                                 drawableRes, theme).mutate();
335                         vectorDrawable.setAllowCaching(false);
336                         vectorDrawable.setCallback(mCallback);
337                         pathErrorScale = vectorDrawable.getPixelSize();
338                         if (state.mVectorDrawable != null) {
339                             state.mVectorDrawable.setCallback(null);
340                         }
341                         state.mVectorDrawable = vectorDrawable;
342                     }
343                     a.recycle();
344                 } else if (TARGET.equals(tagName)) {
345                     final TypedArray a = obtainAttributes(res, theme, attrs,
346                             R.styleable.AnimatedVectorDrawableTarget);
347                     final String target = a.getString(
348                             R.styleable.AnimatedVectorDrawableTarget_name);
349                     final int animResId = a.getResourceId(
350                             R.styleable.AnimatedVectorDrawableTarget_animation, 0);
351                     if (animResId != 0) {
352                         if (theme != null) {
353                             final Animator objectAnimator = AnimatorInflater.loadAnimator(
354                                     res, theme, animResId, pathErrorScale);
355                             state.addTargetAnimator(target, objectAnimator);
356                         } else {
357                             // The animation may be theme-dependent. As a
358                             // workaround until Animator has full support for
359                             // applyTheme(), postpone loading the animator
360                             // until we have a theme in applyTheme().
361                             state.addPendingAnimator(animResId, pathErrorScale, target);
362
363                         }
364                     }
365                     a.recycle();
366                 }
367             }
368
369             eventType = parser.next();
370         }
371
372         // If we don't have any pending animations, we don't need to hold a
373         // reference to the resources.
374         mRes = state.mPendingAnims == null ? null : res;
375     }
376
377     @Override
378     public boolean canApplyTheme() {
379         return (mAnimatedVectorState != null && mAnimatedVectorState.canApplyTheme())
380                 || super.canApplyTheme();
381     }
382
383     @Override
384     public void applyTheme(Theme t) {
385         super.applyTheme(t);
386
387         final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable;
388         if (vectorDrawable != null && vectorDrawable.canApplyTheme()) {
389             vectorDrawable.applyTheme(t);
390         }
391
392         if (t != null) {
393             mAnimatedVectorState.inflatePendingAnimators(t.getResources(), t);
394         }
395
396         // If we don't have any pending animations, we don't need to hold a
397         // reference to the resources.
398         if (mAnimatedVectorState.mPendingAnims == null) {
399             mRes = null;
400         }
401     }
402
403     private static class AnimatedVectorDrawableState extends ConstantState {
404         int mChangingConfigurations;
405         VectorDrawable mVectorDrawable;
406
407         /** Animators that require a theme before inflation. */
408         ArrayList<PendingAnimator> mPendingAnims;
409
410         /** Fully inflated animators awaiting cloning into an AnimatorSet. */
411         ArrayList<Animator> mAnimators;
412
413         /** Map of animators to their target object names */
414         ArrayMap<Animator, String> mTargetNameMap;
415
416         public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
417                 Callback owner, Resources res) {
418             if (copy != null) {
419                 mChangingConfigurations = copy.mChangingConfigurations;
420
421                 if (copy.mVectorDrawable != null) {
422                     final ConstantState cs = copy.mVectorDrawable.getConstantState();
423                     if (res != null) {
424                         mVectorDrawable = (VectorDrawable) cs.newDrawable(res);
425                     } else {
426                         mVectorDrawable = (VectorDrawable) cs.newDrawable();
427                     }
428                     mVectorDrawable = (VectorDrawable) mVectorDrawable.mutate();
429                     mVectorDrawable.setCallback(owner);
430                     mVectorDrawable.setLayoutDirection(copy.mVectorDrawable.getLayoutDirection());
431                     mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
432                     mVectorDrawable.setAllowCaching(false);
433                 }
434
435                 if (copy.mAnimators != null) {
436                     mAnimators = new ArrayList<>(copy.mAnimators);
437                 }
438
439                 if (copy.mTargetNameMap != null) {
440                     mTargetNameMap = new ArrayMap<>(copy.mTargetNameMap);
441                 }
442
443                 if (copy.mPendingAnims != null) {
444                     mPendingAnims = new ArrayList<>(copy.mPendingAnims);
445                 }
446             } else {
447                 mVectorDrawable = new VectorDrawable();
448             }
449         }
450
451         @Override
452         public boolean canApplyTheme() {
453             return (mVectorDrawable != null && mVectorDrawable.canApplyTheme())
454                     || mPendingAnims != null || super.canApplyTheme();
455         }
456
457         @Override
458         public Drawable newDrawable() {
459             return new AnimatedVectorDrawable(this, null);
460         }
461
462         @Override
463         public Drawable newDrawable(Resources res) {
464             return new AnimatedVectorDrawable(this, res);
465         }
466
467         @Override
468         public int getChangingConfigurations() {
469             return mChangingConfigurations;
470         }
471
472         public void addPendingAnimator(int resId, float pathErrorScale, String target) {
473             if (mPendingAnims == null) {
474                 mPendingAnims = new ArrayList<>(1);
475             }
476             mPendingAnims.add(new PendingAnimator(resId, pathErrorScale, target));
477         }
478
479         public void addTargetAnimator(String targetName, Animator animator) {
480             if (mAnimators == null) {
481                 mAnimators = new ArrayList<>(1);
482                 mTargetNameMap = new ArrayMap<>(1);
483             }
484             mAnimators.add(animator);
485             mTargetNameMap.put(animator, targetName);
486
487             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
488                 Log.v(LOGTAG, "add animator  for target " + targetName + " " + animator);
489             }
490         }
491
492         /**
493          * Prepares a local set of mutable animators based on the constant
494          * state.
495          * <p>
496          * If there are any pending uninflated animators, attempts to inflate
497          * them immediately against the provided resources object.
498          *
499          * @param animatorSet the animator set to which the animators should
500          *                    be added
501          * @param res the resources against which to inflate any pending
502          *            animators, or {@code null} if not available
503          */
504         public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
505                 @Nullable Resources res) {
506             // Check for uninflated animators. We can remove this after we add
507             // support for Animator.applyTheme(). See comments in inflate().
508             if (mPendingAnims != null) {
509                 // Attempt to load animators without applying a theme.
510                 if (res != null) {
511                     inflatePendingAnimators(res, null);
512                 } else {
513                     Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable"
514                             + " must be created using a Resources object or applyTheme() must be"
515                             + " called with a non-null Theme object.");
516                 }
517
518                 mPendingAnims = null;
519             }
520
521             // Perform a deep copy of the constant state's animators.
522             final int count = mAnimators == null ? 0 : mAnimators.size();
523             if (count > 0) {
524                 final Animator firstAnim = prepareLocalAnimator(0);
525                 final AnimatorSet.Builder builder = animatorSet.play(firstAnim);
526                 for (int i = 1; i < count; ++i) {
527                     final Animator nextAnim = prepareLocalAnimator(i);
528                     builder.with(nextAnim);
529                 }
530             }
531         }
532
533         /**
534          * Prepares a local animator for the given index within the constant
535          * state's list of animators.
536          *
537          * @param index the index of the animator within the constant state
538          */
539         private Animator prepareLocalAnimator(int index) {
540             final Animator animator = mAnimators.get(index);
541             final Animator localAnimator = animator.clone();
542             final String targetName = mTargetNameMap.get(animator);
543             final Object target = mVectorDrawable.getTargetByName(targetName);
544             localAnimator.setTarget(target);
545             return localAnimator;
546         }
547
548         /**
549          * Inflates pending animators, if any, against a theme. Clears the list of
550          * pending animators.
551          *
552          * @param t the theme against which to inflate the animators
553          */
554         public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) {
555             final ArrayList<PendingAnimator> pendingAnims = mPendingAnims;
556             if (pendingAnims != null) {
557                 mPendingAnims = null;
558
559                 for (int i = 0, count = pendingAnims.size(); i < count; i++) {
560                     final PendingAnimator pendingAnimator = pendingAnims.get(i);
561                     final Animator objectAnimator = pendingAnimator.newInstance(res, t);
562                     addTargetAnimator(pendingAnimator.target, objectAnimator);
563                 }
564             }
565         }
566
567         /**
568          * Basically a constant state for Animators until we actually implement
569          * constant states for Animators.
570          */
571         private static class PendingAnimator {
572             public final int animResId;
573             public final float pathErrorScale;
574             public final String target;
575
576             public PendingAnimator(int animResId, float pathErrorScale, String target) {
577                 this.animResId = animResId;
578                 this.pathErrorScale = pathErrorScale;
579                 this.target = target;
580             }
581
582             public Animator newInstance(Resources res, Theme theme) {
583                 return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale);
584             }
585         }
586     }
587
588     @Override
589     public boolean isRunning() {
590         return mAnimatorSet.isRunning();
591     }
592
593     private boolean isStarted() {
594         return mAnimatorSet.isStarted();
595     }
596
597     /**
598      * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
599      */
600     public void reset() {
601         mAnimatorSet.reset();
602         invalidateSelf();
603     }
604
605     @Override
606     public void start() {
607         ensureAnimatorSet();
608
609         // If any one of the animator has not ended, do nothing.
610         if (isStarted()) {
611             return;
612         }
613
614         mAnimatorSet.start();
615         invalidateSelf();
616     }
617
618     @NonNull
619     private void ensureAnimatorSet() {
620         if (!mHasAnimatorSet) {
621             // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
622             // with a list of LocalAnimators.
623             AnimatorSet set = new AnimatorSet();
624             mAnimatedVectorState.prepareLocalAnimators(set, mRes);
625             mHasAnimatorSet = true;
626             mAnimatorSet.initWithAnimatorSet(set);
627             mRes = null;
628         }
629     }
630
631     @Override
632     public void stop() {
633         mAnimatorSet.end();
634     }
635
636     /**
637      * Reverses ongoing animations or starts pending animations in reverse.
638      * <p>
639      * NOTE: Only works if all animations support reverse. Otherwise, this will
640      * do nothing.
641      * @hide
642      */
643     public void reverse() {
644         ensureAnimatorSet();
645
646         // Only reverse when all the animators can be reversed.
647         if (!canReverse()) {
648             Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
649             return;
650         }
651
652         mAnimatorSet.reverse();
653         invalidateSelf();
654     }
655
656     /**
657      * @hide
658      */
659     public boolean canReverse() {
660         return mAnimatorSet.canReverse();
661     }
662
663     private final Callback mCallback = new Callback() {
664         @Override
665         public void invalidateDrawable(Drawable who) {
666             invalidateSelf();
667         }
668
669         @Override
670         public void scheduleDrawable(Drawable who, Runnable what, long when) {
671             scheduleSelf(what, when);
672         }
673
674         @Override
675         public void unscheduleDrawable(Drawable who, Runnable what) {
676             unscheduleSelf(what);
677         }
678     };
679
680     @Override
681     public void registerAnimationCallback(@NonNull AnimationCallback callback) {
682         if (callback == null) {
683             return;
684         }
685
686         // Add listener accordingly.
687         if (mAnimationCallbacks == null) {
688             mAnimationCallbacks = new ArrayList<>();
689         }
690
691         mAnimationCallbacks.add(callback);
692
693         if (mAnimatorListener == null) {
694             // Create a animator listener and trigger the callback events when listener is
695             // triggered.
696             mAnimatorListener = new AnimatorListenerAdapter() {
697                 @Override
698                 public void onAnimationStart(Animator animation) {
699                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
700                     int size = tmpCallbacks.size();
701                     for (int i = 0; i < size; i ++) {
702                         tmpCallbacks.get(i).onAnimationStart(AnimatedVectorDrawable.this);
703                     }
704                 }
705
706                 @Override
707                 public void onAnimationEnd(Animator animation) {
708                     ArrayList<AnimationCallback> tmpCallbacks = new ArrayList<>(mAnimationCallbacks);
709                     int size = tmpCallbacks.size();
710                     for (int i = 0; i < size; i ++) {
711                         tmpCallbacks.get(i).onAnimationEnd(AnimatedVectorDrawable.this);
712                     }
713                 }
714             };
715         }
716         mAnimatorSet.setListener(mAnimatorListener);
717     }
718
719     // A helper function to clean up the animator listener in the mAnimatorSet.
720     private void removeAnimatorSetListener() {
721         if (mAnimatorListener != null) {
722             mAnimatorSet.removeListener();
723             mAnimatorListener = null;
724         }
725     }
726
727     @Override
728     public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
729         if (mAnimationCallbacks == null || callback == null) {
730             // Nothing to be removed.
731             return false;
732         }
733         boolean removed = mAnimationCallbacks.remove(callback);
734
735         //  When the last call back unregistered, remove the listener accordingly.
736         if (mAnimationCallbacks.size() == 0) {
737             removeAnimatorSetListener();
738         }
739         return removed;
740     }
741
742     @Override
743     public void clearAnimationCallbacks() {
744         removeAnimatorSetListener();
745         if (mAnimationCallbacks == null) {
746             return;
747         }
748
749         mAnimationCallbacks.clear();
750     }
751
752     /**
753      * @hide
754      */
755     public static class VectorDrawableAnimator {
756         private AnimatorListener mListener = null;
757         private final LongArray mStartDelays = new LongArray();
758         private PropertyValuesHolder.PropertyValues mTmpValues =
759                 new PropertyValuesHolder.PropertyValues();
760         private long mSetPtr = 0;
761         private boolean mContainsSequentialAnimators = false;
762         private boolean mStarted = false;
763         private boolean mInitialized = false;
764         private boolean mAnimationPending = false;
765         private boolean mIsReversible = false;
766         // TODO: Consider using NativeAllocationRegistery to track native allocation
767         private final VirtualRefBasePtr mSetRefBasePtr;
768         private WeakReference<RenderNode> mTarget = null;
769         private WeakReference<RenderNode> mLastSeenTarget = null;
770
771
772         VectorDrawableAnimator() {
773             mSetPtr = nCreateAnimatorSet();
774             // Increment ref count on native AnimatorSet, so it doesn't get released before Java
775             // side is done using it.
776             mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
777         }
778
779         private void initWithAnimatorSet(AnimatorSet set) {
780             if (mInitialized) {
781                 // Already initialized
782                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
783                         "re-initialized");
784             }
785             parseAnimatorSet(set, 0);
786             mInitialized = true;
787
788             // Check reversible.
789             if (mContainsSequentialAnimators) {
790                 mIsReversible = false;
791             } else {
792                 // Check if there's any start delay set on child
793                 for (int i = 0; i < mStartDelays.size(); i++) {
794                     if (mStartDelays.get(i) > 0) {
795                         mIsReversible = false;
796                         return;
797                     }
798                 }
799             }
800             mIsReversible = true;
801         }
802
803         private void parseAnimatorSet(AnimatorSet set, long startTime) {
804             ArrayList<Animator> animators = set.getChildAnimations();
805
806             boolean playTogether = set.shouldPlayTogether();
807             // Convert AnimatorSet to VectorDrawableAnimator
808             for (int i = 0; i < animators.size(); i++) {
809                 Animator animator = animators.get(i);
810                 // Here we only support ObjectAnimator
811                 if (animator instanceof AnimatorSet) {
812                     parseAnimatorSet((AnimatorSet) animator, startTime);
813                 } else if (animator instanceof ObjectAnimator) {
814                     createRTAnimator((ObjectAnimator) animator, startTime);
815                 } // ignore ValueAnimators and others because they don't directly modify VD
816                   // therefore will be useless to AVD.
817
818                 if (!playTogether) {
819                     // Assume not play together means play sequentially
820                     startTime += animator.getTotalDuration();
821                     mContainsSequentialAnimators = true;
822                 }
823             }
824         }
825
826         // TODO: This method reads animation data from already parsed Animators. We need to move
827         // this step further up the chain in the parser to avoid the detour.
828         private void createRTAnimator(ObjectAnimator animator, long startTime) {
829             PropertyValuesHolder[] values = animator.getValues();
830             Object target = animator.getTarget();
831             if (target instanceof VectorDrawable.VGroup) {
832                 createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
833                         startTime);
834             } else if (target instanceof VectorDrawable.VPath) {
835                 for (int i = 0; i < values.length; i++) {
836                     values[i].getPropertyValues(mTmpValues);
837                     if (mTmpValues.endValue instanceof PathParser.PathData &&
838                             mTmpValues.propertyName.equals("pathData")) {
839                         createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
840                                 startTime);
841                     }  else if (target instanceof VectorDrawable.VFullPath) {
842                         createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
843                                 startTime);
844                     } else {
845                         throw new IllegalArgumentException("ClipPath only supports PathData " +
846                                 "property");
847                     }
848
849                 }
850             } else if (target instanceof VectorDrawable.VectorDrawableState) {
851                 createRTAnimatorForRootGroup(values, animator,
852                         (VectorDrawable.VectorDrawableState) target, startTime);
853             } else {
854                 // Should never get here
855                 throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
856                         "or ConstantState, " + target.getClass() + " is not supported");
857             }
858         }
859
860         private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
861                 ObjectAnimator animator, VectorDrawable.VGroup target,
862                 long startTime) {
863
864             long nativePtr = target.getNativePtr();
865             int propertyId;
866             for (int i = 0; i < values.length; i++) {
867                 // TODO: We need to support the rare case in AVD where no start value is provided
868                 values[i].getPropertyValues(mTmpValues);
869                 propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
870                 if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
871                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
872                         Log.e(LOGTAG, "Unsupported type: " +
873                                 mTmpValues.type + ". Only float value is supported for Groups.");
874                     }
875                     continue;
876                 }
877                 if (propertyId < 0) {
878                     if (DBG_ANIMATION_VECTOR_DRAWABLE) {
879                         Log.e(LOGTAG, "Unsupported property: " +
880                                 mTmpValues.propertyName + " for Vector Drawable Group");
881                     }
882                     continue;
883                 }
884                 long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
885                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
886                 if (mTmpValues.dataSource != null) {
887                     float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
888                             .getDuration());
889                     nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
890                 }
891                 createNativeChildAnimator(propertyPtr, startTime, animator);
892             }
893         }
894         private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
895                 long startTime) {
896
897             long nativePtr = target.getNativePtr();
898             long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
899                     .getNativePtr();
900             long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
901                     .getNativePtr();
902             long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
903                     endPathDataPtr);
904             createNativeChildAnimator(propertyPtr, startTime, animator);
905         }
906
907         private void createRTAnimatorForFullPath(ObjectAnimator animator,
908                 VectorDrawable.VFullPath target, long startTime) {
909
910             int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
911             long propertyPtr;
912             long nativePtr = target.getNativePtr();
913             if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
914                 if (propertyId < 0) {
915                     throw new IllegalArgumentException("Property: " + mTmpValues
916                             .propertyName + " is not supported for FullPath");
917                 }
918                 propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
919                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
920
921             } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
922                 propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
923                         (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
924             } else {
925                 throw new UnsupportedOperationException("Unsupported type: " +
926                         mTmpValues.type + ". Only float, int or PathData value is " +
927                         "supported for Paths.");
928             }
929             if (mTmpValues.dataSource != null) {
930                 float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
931                         .getDuration());
932                 nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
933             }
934             createNativeChildAnimator(propertyPtr, startTime, animator);
935         }
936
937         private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
938                 ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
939                 long startTime) {
940                 long nativePtr = target.getNativeRenderer();
941                 if (!animator.getPropertyName().equals("alpha")) {
942                     throw new UnsupportedOperationException("Only alpha is supported for root " +
943                             "group");
944                 }
945                 Float startValue = null;
946                 Float endValue = null;
947                 for (int i = 0; i < values.length; i++) {
948                     values[i].getPropertyValues(mTmpValues);
949                     if (mTmpValues.propertyName.equals("alpha")) {
950                         startValue = (Float) mTmpValues.startValue;
951                         endValue = (Float) mTmpValues.endValue;
952                         break;
953                     }
954                 }
955                 if (startValue == null && endValue == null) {
956                     throw new UnsupportedOperationException("No alpha values are specified");
957                 }
958                 long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
959                 createNativeChildAnimator(propertyPtr, startTime, animator);
960         }
961
962         // These are the data points that define the value of the animating properties.
963         // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
964         // a point on the path corresponds to the values of translateX and translateY.
965         // TODO: (Optimization) We should pass the path down in native and chop it into segments
966         // in native.
967         private static float[] createDataPoints(
968                 PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
969             long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
970             int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
971             int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
972             float values[] = new float[numAnimFrames];
973             float lastFrame = numAnimFrames - 1;
974             for (int i = 0; i < numAnimFrames; i++) {
975                 float fraction = i / lastFrame;
976                 values[i] = (Float) dataSource.getValueAtFraction(fraction);
977             }
978             return values;
979         }
980
981         private void createNativeChildAnimator(long propertyPtr, long extraDelay,
982                                                ObjectAnimator animator) {
983             long duration = animator.getDuration();
984             int repeatCount = animator.getRepeatCount();
985             long startDelay = extraDelay + animator.getStartDelay();
986             TimeInterpolator interpolator = animator.getInterpolator();
987             long nativeInterpolator =
988                     RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
989
990             startDelay *= ValueAnimator.getDurationScale();
991             duration *= ValueAnimator.getDurationScale();
992
993             mStartDelays.add(startDelay);
994             nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
995                     repeatCount);
996         }
997
998         /**
999          * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
1000          * in the last draw call), so that when animator set needs to start, we can add the animator
1001          * to the last seen RenderNode target and start right away.
1002          */
1003         protected void recordLastSeenTarget(DisplayListCanvas canvas) {
1004             if (mAnimationPending) {
1005                 mLastSeenTarget = new WeakReference<RenderNode>(
1006                         RenderNodeAnimatorSetHelper.getTarget(canvas));
1007                 if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1008                     Log.d(LOGTAG, "Target is set in the next frame");
1009                 }
1010                 mAnimationPending = false;
1011                 start();
1012             } else {
1013                 mLastSeenTarget = new WeakReference<RenderNode>(
1014                         RenderNodeAnimatorSetHelper.getTarget(canvas));
1015             }
1016
1017         }
1018
1019         private boolean setTarget(RenderNode node) {
1020             if (mTarget != null && mTarget.get() != null) {
1021                 // TODO: Maybe we want to support target change.
1022                 throw new IllegalStateException("Target already set!");
1023             }
1024
1025             node.addAnimator(this);
1026             mTarget = new WeakReference<RenderNode>(node);
1027             return true;
1028         }
1029
1030         private boolean useLastSeenTarget() {
1031             if (mLastSeenTarget != null && mLastSeenTarget.get() != null) {
1032                 setTarget(mLastSeenTarget.get());
1033                 return true;
1034             }
1035             return false;
1036         }
1037
1038         public void start() {
1039             if (!mInitialized) {
1040                 return;
1041             }
1042
1043             if (mStarted) {
1044                 return;
1045             }
1046
1047             if (!useLastSeenTarget()) {
1048                 mAnimationPending = true;
1049                 return;
1050             }
1051
1052             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1053                 Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
1054             }
1055
1056            nStart(mSetPtr, this);
1057             if (mListener != null) {
1058                 mListener.onAnimationStart(null);
1059             }
1060             mStarted = true;
1061         }
1062
1063         public void end() {
1064             if (mInitialized && mStarted) {
1065                 nEnd(mSetPtr);
1066                 onAnimationEnd();
1067             }
1068         }
1069
1070         void reset() {
1071             if (!mInitialized) {
1072                 return;
1073             }
1074             // TODO: Need to implement reset.
1075             Log.w(LOGTAG, "Reset is yet to be implemented");
1076             nReset(mSetPtr);
1077         }
1078
1079         // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
1080         // animators or when the animator set has a start delay
1081         void reverse() {
1082             if (!mIsReversible) {
1083                 return;
1084             }
1085             // TODO: Need to support reverse (non-public API)
1086             Log.w(LOGTAG, "Reverse is yet to be implemented");
1087             nReverse(mSetPtr, this);
1088         }
1089
1090         public long getAnimatorNativePtr() {
1091             return mSetPtr;
1092         }
1093
1094         boolean canReverse() {
1095             return mIsReversible;
1096         }
1097
1098         boolean isStarted() {
1099             return mStarted;
1100         }
1101
1102         boolean isRunning() {
1103             if (!mInitialized) {
1104                 return false;
1105             }
1106             return mStarted;
1107         }
1108
1109         void setListener(AnimatorListener listener) {
1110             mListener = listener;
1111         }
1112
1113         void removeListener() {
1114             mListener = null;
1115         }
1116
1117         private void onAnimationEnd() {
1118             mStarted = false;
1119             if (mListener != null) {
1120                 mListener.onAnimationEnd(null);
1121             }
1122             mTarget = null;
1123         }
1124
1125         // onFinished: should be called from native
1126         private static void callOnFinished(VectorDrawableAnimator set) {
1127             if (DBG_ANIMATION_VECTOR_DRAWABLE) {
1128                 Log.d(LOGTAG, "on finished called from native");
1129             }
1130             set.onAnimationEnd();
1131         }
1132     }
1133
1134     private static native long nCreateAnimatorSet();
1135     private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
1136              long nativeInterpolator, long startDelay, long duration, int repeatCount);
1137
1138     private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
1139             float startValue, float endValue);
1140
1141     private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
1142             long endValuePtr);
1143     private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
1144             int startValue, int endValue);
1145     private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
1146             float startValue, float endValue);
1147     private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
1148             float endValue);
1149     private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
1150     private static native void nStart(long animatorSetPtr, VectorDrawableAnimator set);
1151     private static native void nReverse(long animatorSetPtr, VectorDrawableAnimator set);
1152     private static native void nEnd(long animatorSetPtr);
1153     private static native void nReset(long animatorSetPtr);
1154 }