Home | History | Annotate | Download | only in view
      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 
    106     public static int mapViewPropertyToRenderProperty(int viewProperty) {
    107         return sViewPropertyAnimatorMap.get(viewProperty);
    108     }
    109 
    110     public RenderNodeAnimator(int property, float finalValue) {
    111         mRenderProperty = property;
    112         mFinalValue = finalValue;
    113         mUiThreadHandlesDelay = true;
    114         init(nCreateAnimator(property, finalValue));
    115     }
    116 
    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      */
    131     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
    132         init(nCreateCanvasPropertyPaintAnimator(
    133                 property.getNativeContainer(), paintField, finalValue));
    134         mUiThreadHandlesDelay = false;
    135     }
    136 
    137     public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
    138         init(nCreateRevealAnimator(x, y, startRadius, endRadius));
    139         mUiThreadHandlesDelay = true;
    140     }
    141 
    142     private void init(long ptr) {
    143         mNativePtr = new VirtualRefBasePtr(ptr);
    144     }
    145 
    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 
    156     static boolean isNativeInterpolator(TimeInterpolator interpolator) {
    157         return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
    158     }
    159 
    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
    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 
    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 
    214     private void moveToRunningState() {
    215         mState = STATE_RUNNING;
    216         if (mNativePtr != null) {
    217             nStart(mNativePtr.get());
    218         }
    219         notifyStartListeners();
    220     }
    221 
    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
    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
    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
    269     public void pause() {
    270         throw new UnsupportedOperationException();
    271     }
    272 
    273     @Override
    274     public void resume() {
    275         throw new UnsupportedOperationException();
    276     }
    277 
    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 */
    284     public void setTarget(DisplayListCanvas canvas) {
    285         setTarget(canvas.mNode);
    286     }
    287 
    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 
    298     public void setStartValue(float startValue) {
    299         checkMutable();
    300         nSetStartValue(mNativePtr.get(), startValue);
    301     }
    302 
    303     @Override
    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
    314     public long getStartDelay() {
    315         return mUnscaledStartDelay;
    316     }
    317 
    318     @Override
    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
    330     public long getDuration() {
    331         return mUnscaledDuration;
    332     }
    333 
    334     @Override
    335     public long getTotalDuration() {
    336         return mUnscaledDuration + mUnscaledStartDelay;
    337     }
    338 
    339     @Override
    340     public boolean isRunning() {
    341         return mState == STATE_DELAYED || mState == STATE_RUNNING;
    342     }
    343 
    344     @Override
    345     public boolean isStarted() {
    346         return mState != STATE_PREPARE;
    347     }
    348 
    349     @Override
    350     public void setInterpolator(TimeInterpolator interpolator) {
    351         checkMutable();
    352         mInterpolator = interpolator;
    353     }
    354 
    355     @Override
    356     public TimeInterpolator getInterpolator() {
    357         return mInterpolator;
    358     }
    359 
    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 
    385     private void releaseNativePtr() {
    386         if (mNativePtr != null) {
    387             mNativePtr.release();
    388             mNativePtr = null;
    389         }
    390     }
    391 
    392     @SuppressWarnings("unchecked")
    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 
    401     long getNativeAnimator() {
    402         return mNativePtr.get();
    403     }
    404 
    405     /**
    406      * @return true if the animator was started, false if still delayed
    407      */
    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 
    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 
    436         public DelayedAnimationHelper() {
    437             mChoreographer = Choreographer.getInstance();
    438         }
    439 
    440         public void addDelayedAnimation(RenderNodeAnimator animator) {
    441             mDelayedAnims.add(animator);
    442             scheduleCallback();
    443         }
    444 
    445         public void removeDelayedAnimation(RenderNodeAnimator animator) {
    446             mDelayedAnims.remove(animator);
    447         }
    448 
    449         private void scheduleCallback() {
    450             if (!mCallbackScheduled) {
    451                 mCallbackScheduled = true;
    452                 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
    453             }
    454         }
    455 
    456         @Override
    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
    482     private static void callOnFinished(RenderNodeAnimator animator) {
    483         animator.onFinished();
    484     }
    485 
    486     @Override
    487     public Animator clone() {
    488         throw new IllegalStateException("Cannot clone this animator");
    489     }
    490 
    491     @Override
    492     public void setAllowRunningAsynchronously(boolean mayRunAsync) {
    493         checkMutable();
    494         nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
    495     }
    496 
    497     private static native long nCreateAnimator(int property, float finalValue);
    498     private static native long nCreateCanvasPropertyFloatAnimator(
    499             long canvasProperty, float finalValue);
    500     private static native long nCreateCanvasPropertyPaintAnimator(
    501             long canvasProperty, int paintField, float finalValue);
    502     private static native long nCreateRevealAnimator(
    503             int x, int y, float startRadius, float endRadius);
    504 
    505     private static native void nSetStartValue(long nativePtr, float startValue);
    506     private static native void nSetDuration(long nativePtr, long duration);
    507     private static native long nGetDuration(long nativePtr);
    508     private static native void nSetStartDelay(long nativePtr, long startDelay);
    509     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
    510     private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
    511     private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
    512 
    513     private static native void nStart(long animPtr);
    514     private static native void nEnd(long animPtr);
    515 }
    516