• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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