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