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