1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.animation.Animator; 20 import android.animation.TimeInterpolator; 21 import android.animation.ValueAnimator; 22 import android.graphics.CanvasProperty; 23 import android.graphics.Paint; 24 import android.util.SparseIntArray; 25 26 import com.android.internal.util.VirtualRefBasePtr; 27 import com.android.internal.view.animation.FallbackLUTInterpolator; 28 import com.android.internal.view.animation.HasNativeInterpolator; 29 import com.android.internal.view.animation.NativeInterpolatorFactory; 30 31 import java.util.ArrayList; 32 33 /** 34 * @hide 35 */ 36 public class RenderNodeAnimator extends Animator { 37 // Keep in sync with enum RenderProperty in Animator.h 38 public static final int TRANSLATION_X = 0; 39 public static final int TRANSLATION_Y = 1; 40 public static final int TRANSLATION_Z = 2; 41 public static final int SCALE_X = 3; 42 public static final int SCALE_Y = 4; 43 public static final int ROTATION = 5; 44 public static final int ROTATION_X = 6; 45 public static final int ROTATION_Y = 7; 46 public static final int X = 8; 47 public static final int Y = 9; 48 public static final int Z = 10; 49 public static final int ALPHA = 11; 50 // The last value in the enum, used for array size initialization 51 public static final int LAST_VALUE = ALPHA; 52 53 // Keep in sync with enum PaintFields in Animator.h 54 public static final int PAINT_STROKE_WIDTH = 0; 55 56 /** 57 * Field for the Paint alpha channel, which should be specified as a value 58 * between 0 and 255. 59 */ 60 public static final int PAINT_ALPHA = 1; 61 62 // ViewPropertyAnimator uses a mask for its values, we need to remap them 63 // to the enum values here. RenderPropertyAnimator can't use the mask values 64 // directly as internally it uses a lookup table so it needs the values to 65 // be sequential starting from 0 66 private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{ 67 put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X); 68 put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y); 69 put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z); 70 put(ViewPropertyAnimator.SCALE_X, SCALE_X); 71 put(ViewPropertyAnimator.SCALE_Y, SCALE_Y); 72 put(ViewPropertyAnimator.ROTATION, ROTATION); 73 put(ViewPropertyAnimator.ROTATION_X, ROTATION_X); 74 put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y); 75 put(ViewPropertyAnimator.X, X); 76 put(ViewPropertyAnimator.Y, Y); 77 put(ViewPropertyAnimator.Z, Z); 78 put(ViewPropertyAnimator.ALPHA, ALPHA); 79 }}; 80 81 private VirtualRefBasePtr mNativePtr; 82 83 private RenderNode mTarget; 84 private View mViewTarget; 85 private int mRenderProperty = -1; 86 private float mFinalValue; 87 private TimeInterpolator mInterpolator; 88 89 private static final int STATE_PREPARE = 0; 90 private static final int STATE_DELAYED = 1; 91 private static final int STATE_RUNNING = 2; 92 private static final int STATE_FINISHED = 3; 93 private int mState = STATE_PREPARE; 94 95 private long mUnscaledDuration = 300; 96 private long mUnscaledStartDelay = 0; 97 // If this is true, we will run any start delays on the UI thread. This is 98 // the safe default, and is necessary to ensure start listeners fire at 99 // the correct time. Animators created by RippleDrawable (the 100 // CanvasProperty<> ones) do not have this expectation, and as such will 101 // set this to false so that the renderthread handles the startdelay instead 102 private final boolean mUiThreadHandlesDelay; 103 private long mStartDelay = 0; 104 private long mStartTime; 105 mapViewPropertyToRenderProperty(int viewProperty)106 public static int mapViewPropertyToRenderProperty(int viewProperty) { 107 return sViewPropertyAnimatorMap.get(viewProperty); 108 } 109 RenderNodeAnimator(int property, float finalValue)110 public RenderNodeAnimator(int property, float finalValue) { 111 mRenderProperty = property; 112 mFinalValue = finalValue; 113 mUiThreadHandlesDelay = true; 114 init(nCreateAnimator(property, finalValue)); 115 } 116 RenderNodeAnimator(CanvasProperty<Float> property, float finalValue)117 public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { 118 init(nCreateCanvasPropertyFloatAnimator( 119 property.getNativeContainer(), finalValue)); 120 mUiThreadHandlesDelay = false; 121 } 122 123 /** 124 * Creates a new render node animator for a field on a Paint property. 125 * 126 * @param property The paint property to target 127 * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or 128 * {@link #PAINT_STROKE_WIDTH} 129 * @param finalValue The target value for the property 130 */ RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue)131 public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { 132 init(nCreateCanvasPropertyPaintAnimator( 133 property.getNativeContainer(), paintField, finalValue)); 134 mUiThreadHandlesDelay = false; 135 } 136 RenderNodeAnimator(int x, int y, float startRadius, float endRadius)137 public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) { 138 init(nCreateRevealAnimator(x, y, startRadius, endRadius)); 139 mUiThreadHandlesDelay = true; 140 } 141 init(long ptr)142 private void init(long ptr) { 143 mNativePtr = new VirtualRefBasePtr(ptr); 144 } 145 checkMutable()146 private void checkMutable() { 147 if (mState != STATE_PREPARE) { 148 throw new IllegalStateException("Animator has already started, cannot change it now!"); 149 } 150 if (mNativePtr == null) { 151 throw new IllegalStateException("Animator's target has been destroyed " 152 + "(trying to modify an animation after activity destroy?)"); 153 } 154 } 155 isNativeInterpolator(TimeInterpolator interpolator)156 static boolean isNativeInterpolator(TimeInterpolator interpolator) { 157 return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class); 158 } 159 applyInterpolator()160 private void applyInterpolator() { 161 if (mInterpolator == null || mNativePtr == null) return; 162 163 long ni; 164 if (isNativeInterpolator(mInterpolator)) { 165 ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator(); 166 } else { 167 long duration = nGetDuration(mNativePtr.get()); 168 ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration); 169 } 170 nSetInterpolator(mNativePtr.get(), ni); 171 } 172 173 @Override start()174 public void start() { 175 if (mTarget == null) { 176 throw new IllegalStateException("Missing target!"); 177 } 178 179 if (mState != STATE_PREPARE) { 180 throw new IllegalStateException("Already started!"); 181 } 182 183 mState = STATE_DELAYED; 184 applyInterpolator(); 185 186 if (mNativePtr == null) { 187 // It's dead, immediately cancel 188 cancel(); 189 } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) { 190 nSetStartDelay(mNativePtr.get(), mStartDelay); 191 doStart(); 192 } else { 193 getHelper().addDelayedAnimation(this); 194 } 195 } 196 doStart()197 private void doStart() { 198 // Alpha is a special snowflake that has the canonical value stored 199 // in mTransformationInfo instead of in RenderNode, so we need to update 200 // it with the final value here. 201 if (mRenderProperty == RenderNodeAnimator.ALPHA) { 202 mViewTarget.ensureTransformationInfo(); 203 mViewTarget.mTransformationInfo.mAlpha = mFinalValue; 204 } 205 206 moveToRunningState(); 207 208 if (mViewTarget != null) { 209 // Kick off a frame to start the process 210 mViewTarget.invalidateViewProperty(true, false); 211 } 212 } 213 moveToRunningState()214 private void moveToRunningState() { 215 mState = STATE_RUNNING; 216 if (mNativePtr != null) { 217 nStart(mNativePtr.get()); 218 } 219 notifyStartListeners(); 220 } 221 notifyStartListeners()222 private void notifyStartListeners() { 223 final ArrayList<AnimatorListener> listeners = cloneListeners(); 224 final int numListeners = listeners == null ? 0 : listeners.size(); 225 for (int i = 0; i < numListeners; i++) { 226 listeners.get(i).onAnimationStart(this); 227 } 228 } 229 230 @Override cancel()231 public void cancel() { 232 if (mState != STATE_PREPARE && mState != STATE_FINISHED) { 233 if (mState == STATE_DELAYED) { 234 getHelper().removeDelayedAnimation(this); 235 moveToRunningState(); 236 } 237 238 final ArrayList<AnimatorListener> listeners = cloneListeners(); 239 final int numListeners = listeners == null ? 0 : listeners.size(); 240 for (int i = 0; i < numListeners; i++) { 241 listeners.get(i).onAnimationCancel(this); 242 } 243 244 end(); 245 } 246 } 247 248 @Override end()249 public void end() { 250 if (mState != STATE_FINISHED) { 251 if (mState < STATE_RUNNING) { 252 getHelper().removeDelayedAnimation(this); 253 doStart(); 254 } 255 if (mNativePtr != null) { 256 nEnd(mNativePtr.get()); 257 if (mViewTarget != null) { 258 // Kick off a frame to flush the state change 259 mViewTarget.invalidateViewProperty(true, false); 260 } 261 } else { 262 // It's already dead, jump to onFinish 263 onFinished(); 264 } 265 } 266 } 267 268 @Override pause()269 public void pause() { 270 throw new UnsupportedOperationException(); 271 } 272 273 @Override resume()274 public void resume() { 275 throw new UnsupportedOperationException(); 276 } 277 setTarget(View view)278 public void setTarget(View view) { 279 mViewTarget = view; 280 setTarget(mViewTarget.mRenderNode); 281 } 282 283 /** Sets the animation target to the owning view of the DisplayListCanvas */ setTarget(DisplayListCanvas canvas)284 public void setTarget(DisplayListCanvas canvas) { 285 setTarget(canvas.mNode); 286 } 287 setTarget(RenderNode node)288 private void setTarget(RenderNode node) { 289 checkMutable(); 290 if (mTarget != null) { 291 throw new IllegalStateException("Target already set!"); 292 } 293 nSetListener(mNativePtr.get(), this); 294 mTarget = node; 295 mTarget.addAnimator(this); 296 } 297 setStartValue(float startValue)298 public void setStartValue(float startValue) { 299 checkMutable(); 300 nSetStartValue(mNativePtr.get(), startValue); 301 } 302 303 @Override setStartDelay(long startDelay)304 public void setStartDelay(long startDelay) { 305 checkMutable(); 306 if (startDelay < 0) { 307 throw new IllegalArgumentException("startDelay must be positive; " + startDelay); 308 } 309 mUnscaledStartDelay = startDelay; 310 mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay); 311 } 312 313 @Override getStartDelay()314 public long getStartDelay() { 315 return mUnscaledStartDelay; 316 } 317 318 @Override setDuration(long duration)319 public RenderNodeAnimator setDuration(long duration) { 320 checkMutable(); 321 if (duration < 0) { 322 throw new IllegalArgumentException("duration must be positive; " + duration); 323 } 324 mUnscaledDuration = duration; 325 nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale())); 326 return this; 327 } 328 329 @Override getDuration()330 public long getDuration() { 331 return mUnscaledDuration; 332 } 333 334 @Override getTotalDuration()335 public long getTotalDuration() { 336 return mUnscaledDuration + mUnscaledStartDelay; 337 } 338 339 @Override isRunning()340 public boolean isRunning() { 341 return mState == STATE_DELAYED || mState == STATE_RUNNING; 342 } 343 344 @Override isStarted()345 public boolean isStarted() { 346 return mState != STATE_PREPARE; 347 } 348 349 @Override setInterpolator(TimeInterpolator interpolator)350 public void setInterpolator(TimeInterpolator interpolator) { 351 checkMutable(); 352 mInterpolator = interpolator; 353 } 354 355 @Override getInterpolator()356 public TimeInterpolator getInterpolator() { 357 return mInterpolator; 358 } 359 onFinished()360 protected void onFinished() { 361 if (mState == STATE_PREPARE) { 362 // Unlikely but possible, the native side has been destroyed 363 // before we have started. 364 releaseNativePtr(); 365 return; 366 } 367 if (mState == STATE_DELAYED) { 368 getHelper().removeDelayedAnimation(this); 369 notifyStartListeners(); 370 } 371 mState = STATE_FINISHED; 372 373 final ArrayList<AnimatorListener> listeners = cloneListeners(); 374 final int numListeners = listeners == null ? 0 : listeners.size(); 375 for (int i = 0; i < numListeners; i++) { 376 listeners.get(i).onAnimationEnd(this); 377 } 378 379 // Release the native object, as it has a global reference to us. This 380 // breaks the cyclic reference chain, and allows this object to be 381 // GC'd 382 releaseNativePtr(); 383 } 384 releaseNativePtr()385 private void releaseNativePtr() { 386 if (mNativePtr != null) { 387 mNativePtr.release(); 388 mNativePtr = null; 389 } 390 } 391 392 @SuppressWarnings("unchecked") cloneListeners()393 private ArrayList<AnimatorListener> cloneListeners() { 394 ArrayList<AnimatorListener> listeners = getListeners(); 395 if (listeners != null) { 396 listeners = (ArrayList<AnimatorListener>) listeners.clone(); 397 } 398 return listeners; 399 } 400 getNativeAnimator()401 long getNativeAnimator() { 402 return mNativePtr.get(); 403 } 404 405 /** 406 * @return true if the animator was started, false if still delayed 407 */ processDelayed(long frameTimeMs)408 private boolean processDelayed(long frameTimeMs) { 409 if (mStartTime == 0) { 410 mStartTime = frameTimeMs; 411 } else if ((frameTimeMs - mStartTime) >= mStartDelay) { 412 doStart(); 413 return true; 414 } 415 return false; 416 } 417 getHelper()418 private static DelayedAnimationHelper getHelper() { 419 DelayedAnimationHelper helper = sAnimationHelper.get(); 420 if (helper == null) { 421 helper = new DelayedAnimationHelper(); 422 sAnimationHelper.set(helper); 423 } 424 return helper; 425 } 426 427 private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper = 428 new ThreadLocal<DelayedAnimationHelper>(); 429 430 private static class DelayedAnimationHelper implements Runnable { 431 432 private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>(); 433 private final Choreographer mChoreographer; 434 private boolean mCallbackScheduled; 435 DelayedAnimationHelper()436 public DelayedAnimationHelper() { 437 mChoreographer = Choreographer.getInstance(); 438 } 439 addDelayedAnimation(RenderNodeAnimator animator)440 public void addDelayedAnimation(RenderNodeAnimator animator) { 441 mDelayedAnims.add(animator); 442 scheduleCallback(); 443 } 444 removeDelayedAnimation(RenderNodeAnimator animator)445 public void removeDelayedAnimation(RenderNodeAnimator animator) { 446 mDelayedAnims.remove(animator); 447 } 448 scheduleCallback()449 private void scheduleCallback() { 450 if (!mCallbackScheduled) { 451 mCallbackScheduled = true; 452 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); 453 } 454 } 455 456 @Override run()457 public void run() { 458 long frameTimeMs = mChoreographer.getFrameTime(); 459 mCallbackScheduled = false; 460 461 int end = 0; 462 for (int i = 0; i < mDelayedAnims.size(); i++) { 463 RenderNodeAnimator animator = mDelayedAnims.get(i); 464 if (!animator.processDelayed(frameTimeMs)) { 465 if (end != i) { 466 mDelayedAnims.set(end, animator); 467 } 468 end++; 469 } 470 } 471 while (mDelayedAnims.size() > end) { 472 mDelayedAnims.remove(mDelayedAnims.size() - 1); 473 } 474 475 if (mDelayedAnims.size() > 0) { 476 scheduleCallback(); 477 } 478 } 479 } 480 481 // Called by native callOnFinished(RenderNodeAnimator animator)482 private static void callOnFinished(RenderNodeAnimator animator) { 483 animator.onFinished(); 484 } 485 486 @Override clone()487 public Animator clone() { 488 throw new IllegalStateException("Cannot clone this animator"); 489 } 490 491 @Override setAllowRunningAsynchronously(boolean mayRunAsync)492 public void setAllowRunningAsynchronously(boolean mayRunAsync) { 493 checkMutable(); 494 nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync); 495 } 496 nCreateAnimator(int property, float finalValue)497 private static native long nCreateAnimator(int property, float finalValue); nCreateCanvasPropertyFloatAnimator( long canvasProperty, float finalValue)498 private static native long nCreateCanvasPropertyFloatAnimator( 499 long canvasProperty, float finalValue); nCreateCanvasPropertyPaintAnimator( long canvasProperty, int paintField, float finalValue)500 private static native long nCreateCanvasPropertyPaintAnimator( 501 long canvasProperty, int paintField, float finalValue); nCreateRevealAnimator( int x, int y, float startRadius, float endRadius)502 private static native long nCreateRevealAnimator( 503 int x, int y, float startRadius, float endRadius); 504 nSetStartValue(long nativePtr, float startValue)505 private static native void nSetStartValue(long nativePtr, float startValue); nSetDuration(long nativePtr, long duration)506 private static native void nSetDuration(long nativePtr, long duration); nGetDuration(long nativePtr)507 private static native long nGetDuration(long nativePtr); nSetStartDelay(long nativePtr, long startDelay)508 private static native void nSetStartDelay(long nativePtr, long startDelay); nSetInterpolator(long animPtr, long interpolatorPtr)509 private static native void nSetInterpolator(long animPtr, long interpolatorPtr); nSetAllowRunningAsync(long animPtr, boolean mayRunAsync)510 private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync); nSetListener(long animPtr, RenderNodeAnimator listener)511 private static native void nSetListener(long animPtr, RenderNodeAnimator listener); 512 nStart(long animPtr)513 private static native void nStart(long animPtr); nEnd(long animPtr)514 private static native void nEnd(long animPtr); 515 } 516