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