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