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