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