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