Run RevealAnimator on RT

 Bug: 16894195

Change-Id: I9acffa7aecc04c68b60c67f8f8a20aa225075a42
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 5f80ed7..3720c81 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -370,6 +370,7 @@
      * @hide
      */
     public void reverse() {
+        throw new IllegalStateException("Reverse is not supported");
     }
 
     /**
diff --git a/core/java/android/animation/RevealAnimator.java b/core/java/android/animation/RevealAnimator.java
index e363a77..0f85f49 100644
--- a/core/java/android/animation/RevealAnimator.java
+++ b/core/java/android/animation/RevealAnimator.java
@@ -26,148 +26,21 @@
  *
  * @hide
  */
-public class RevealAnimator extends ValueAnimator {
+public class RevealAnimator extends RenderNodeAnimator {
 
     private View mClipView;
-    private int mX, mY;
-    private float mStartRadius, mEndRadius;
-    private float mDelta;
-    private boolean mMayRunAsync;
-
-    // If this is null, we are running on the UI thread driven by the base
-    // ValueAnimator class. If this is not null, forward requests on to this
-    // Animator instead.
-    private RenderNodeAnimator mRtAnimator;
 
     public RevealAnimator(View clipView, int x, int y,
             float startRadius, float endRadius) {
+        super(x, y, startRadius, endRadius);
         mClipView = clipView;
-        mStartRadius = startRadius;
-        mEndRadius = endRadius;
-        mDelta = endRadius - startRadius;
-        mX = x;
-        mY = y;
-        super.setValues(PropertyValuesHolder.ofFloat("radius", startRadius, endRadius));
+        setTarget(mClipView);
     }
 
     @Override
-    void animateValue(float fraction) {
-        super.animateValue(fraction);
-        fraction = getAnimatedFraction();
-        float radius = mStartRadius + (mDelta * fraction);
-        mClipView.setRevealClip(true, mX, mY, radius);
-    }
-
-    @Override
-    protected void endAnimation(AnimationHandler handler) {
+    protected void onFinished() {
         mClipView.setRevealClip(false, 0, 0, 0);
-        super.endAnimation(handler);
+        super.onFinished();
     }
 
-    @Override
-    public void setAllowRunningAsynchronously(boolean mayRunAsync) {
-        mMayRunAsync = mayRunAsync;
-    }
-
-    private boolean canRunAsync() {
-        if (!mMayRunAsync) {
-            return false;
-        }
-        if (mUpdateListeners != null && mUpdateListeners.size() > 0) {
-            return false;
-        }
-        // TODO: Have RNA support this
-        if (getRepeatCount() != 0) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void start() {
-        if (mRtAnimator != null) {
-            mRtAnimator.end();
-            mRtAnimator = null;
-        }
-        if (canRunAsync()) {
-            mRtAnimator = new RenderNodeAnimator(mX, mY, mStartRadius, mEndRadius);
-            mRtAnimator.setDuration(getDuration());
-            mRtAnimator.setInterpolator(getInterpolator());
-            mRtAnimator.setTarget(mClipView);
-            // TODO: Listeners
-            mRtAnimator.start();
-        } else {
-            super.start();
-        }
-    }
-
-    @Override
-    public void cancel() {
-        if (mRtAnimator != null) {
-            mRtAnimator.cancel();
-        } else {
-            super.cancel();
-        }
-    }
-
-    @Override
-    public void end() {
-        if (mRtAnimator != null) {
-            mRtAnimator.end();
-        } else {
-            super.end();
-        }
-    }
-
-    @Override
-    public void resume() {
-        if (mRtAnimator != null) {
-            // TODO: Support? Reject?
-        } else {
-            super.resume();
-        }
-    }
-
-    @Override
-    public void pause() {
-        if (mRtAnimator != null) {
-            // TODO: see resume()
-        } else {
-            super.pause();
-        }
-    }
-
-    @Override
-    public boolean isRunning() {
-        if (mRtAnimator != null) {
-            return mRtAnimator.isRunning();
-        } else {
-            return super.isRunning();
-        }
-    }
-
-    @Override
-    public boolean isStarted() {
-        if (mRtAnimator != null) {
-            return mRtAnimator.isStarted();
-        } else {
-            return super.isStarted();
-        }
-    }
-
-    @Override
-    public void reverse() {
-        if (mRtAnimator != null) {
-            // TODO support
-        } else {
-            super.reverse();
-        }
-    }
-
-    @Override
-    public RevealAnimator clone() {
-        RevealAnimator anim = (RevealAnimator) super.clone();
-        anim.mRtAnimator = null;
-        return anim;
-    }
 }
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c7f16e0..a56d448 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -93,6 +93,14 @@
 
     private long mUnscaledDuration = 300;
     private long mUnscaledStartDelay = 0;
+    // If this is true, we will run any start delays on the UI thread. This is
+    // the safe default, and is necessary to ensure start listeners fire at
+    // the correct time. Animators created by RippleDrawable (the
+    // CanvasProperty<> ones) do not have this expectation, and as such will
+    // set this to false so that the renderthread handles the startdelay instead
+    private final boolean mUiThreadHandlesDelay;
+    private long mStartDelay = 0;
+    private long mStartTime;
 
     public static int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
@@ -101,6 +109,7 @@
     public RenderNodeAnimator(int property, float finalValue) {
         mRenderProperty = property;
         mFinalValue = finalValue;
+        mUiThreadHandlesDelay = true;
         init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
                 property, finalValue));
     }
@@ -109,6 +118,7 @@
         init(nCreateCanvasPropertyFloatAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
                 property.getNativeContainer(), finalValue));
+        mUiThreadHandlesDelay = false;
     }
 
     /**
@@ -123,11 +133,13 @@
         init(nCreateCanvasPropertyPaintAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
                 property.getNativeContainer(), paintField, finalValue));
+        mUiThreadHandlesDelay = false;
     }
 
     public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
         init(nCreateRevealAnimator(new WeakReference<RenderNodeAnimator>(this),
                 x, y, startRadius, endRadius));
+        mUiThreadHandlesDelay = true;
     }
 
     private void init(long ptr) {
@@ -169,6 +181,16 @@
 
         mStarted = true;
         applyInterpolator();
+
+        if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+            nSetStartDelay(mNativePtr.get(), mStartDelay);
+            doStart();
+        } else {
+            getHelper().addDelayedAnimation(this);
+        }
+    }
+
+    private void doStart() {
         nStart(mNativePtr.get());
 
         // Alpha is a special snowflake that has the canonical value stored
@@ -195,6 +217,7 @@
     @Override
     public void cancel() {
         if (!mFinished) {
+            getHelper().removeDelayedAnimation(this);
             nEnd(mNativePtr.get());
 
             final ArrayList<AnimatorListener> listeners = getListeners();
@@ -258,7 +281,7 @@
             throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
         }
         mUnscaledStartDelay = startDelay;
-        nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale()));
+        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
     }
 
     @Override
@@ -303,7 +326,7 @@
         return mInterpolator;
     }
 
-    private void onFinished() {
+    protected void onFinished() {
         mFinished = true;
 
         final ArrayList<AnimatorListener> listeners = getListeners();
@@ -317,6 +340,82 @@
         return mNativePtr.get();
     }
 
+    /**
+     * @return true if the animator was started, false if still delayed
+     */
+    private boolean processDelayed(long frameTimeMs) {
+        if (mStartTime == 0) {
+            mStartTime = frameTimeMs;
+        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+            doStart();
+            return true;
+        }
+        return false;
+    }
+
+    private static DelayedAnimationHelper getHelper() {
+        DelayedAnimationHelper helper = sAnimationHelper.get();
+        if (helper == null) {
+            helper = new DelayedAnimationHelper();
+            sAnimationHelper.set(helper);
+        }
+        return helper;
+    }
+
+    private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
+            new ThreadLocal<DelayedAnimationHelper>();
+
+    private static class DelayedAnimationHelper implements Runnable {
+
+        private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
+        private final Choreographer mChoreographer;
+        private boolean mCallbackScheduled;
+
+        public DelayedAnimationHelper() {
+            mChoreographer = Choreographer.getInstance();
+        }
+
+        public void addDelayedAnimation(RenderNodeAnimator animator) {
+            mDelayedAnims.add(animator);
+            scheduleCallback();
+        }
+
+        public void removeDelayedAnimation(RenderNodeAnimator animator) {
+            mDelayedAnims.remove(animator);
+        }
+
+        private void scheduleCallback() {
+            if (!mCallbackScheduled) {
+                mCallbackScheduled = true;
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+            }
+        }
+
+        @Override
+        public void run() {
+            long frameTimeMs = mChoreographer.getFrameTime();
+            mCallbackScheduled = false;
+
+            int end = 0;
+            for (int i = 0; i < mDelayedAnims.size(); i++) {
+                RenderNodeAnimator animator = mDelayedAnims.get(i);
+                if (!animator.processDelayed(frameTimeMs)) {
+                    if (end != i) {
+                        mDelayedAnims.set(end, animator);
+                    }
+                    end++;
+                }
+            }
+            while (mDelayedAnims.size() > end) {
+                mDelayedAnims.remove(mDelayedAnims.size() - 1);
+            }
+
+            if (mDelayedAnims.size() > 0) {
+                scheduleCallback();
+            }
+        }
+    }
+
     // Called by native
     private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) {
         RenderNodeAnimator animator = weakThis.get();
@@ -325,6 +424,11 @@
         }
     }
 
+    @Override
+    public Animator clone() {
+        throw new IllegalStateException("Cannot clone this animator");
+    }
+
     private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
             int property, float finalValue);
     private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java
deleted file mode 100644
index 8103f47..0000000
--- a/core/java/android/view/RenderNodeAnimatorCompat.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.animation.ValueAnimator;
-
-import java.util.ArrayList;
-
-/**
- * This class provides compatibility for things like start listeners &
- * start delays for use by ViewPropertyAnimator and ObjectAnimator
- * @hide
- */
-public class RenderNodeAnimatorCompat extends RenderNodeAnimator {
-
-    private long mUnscaledStartDelay = 0;
-    private long mStartDelay = 0;
-    private long mStartTime;
-    private boolean mCanceled;
-    private boolean mStarted;
-
-    public RenderNodeAnimatorCompat(int property, float finalValue) {
-        super(property, finalValue);
-    }
-
-    @Override
-    public void setStartDelay(long startDelay) {
-        mUnscaledStartDelay = startDelay;
-        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
-    }
-
-    @Override
-    public long getStartDelay() {
-        return mUnscaledStartDelay;
-    }
-
-    @Override
-    public void start() {
-        mStarted = true;
-        if (mStartDelay <= 0) {
-            doStart();
-        } else {
-            getHelper().addDelayedAnimation(this);
-        }
-    }
-
-    @Override
-    public boolean isStarted() {
-        return mStarted;
-    }
-
-    private void doStart() {
-        if (!mCanceled) {
-            super.start();
-        }
-    }
-
-    @Override
-    public void cancel() {
-        mCanceled = true;
-        super.cancel();
-    }
-
-    /**
-     * @return true if the animator was started, false if still delayed
-     */
-    private boolean processDelayed(long frameTimeMs) {
-        if (mCanceled) return true;
-
-        if (mStartTime == 0) {
-            mStartTime = frameTimeMs;
-        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
-            doStart();
-            return true;
-        }
-        return false;
-    }
-
-    private static AnimationHelper getHelper() {
-        AnimationHelper helper = sAnimationHelper.get();
-        if (helper == null) {
-            helper = new AnimationHelper();
-            sAnimationHelper.set(helper);
-        }
-        return helper;
-    }
-
-    private static ThreadLocal<AnimationHelper> sAnimationHelper =
-            new ThreadLocal<AnimationHelper>();
-
-    private static class AnimationHelper implements Runnable {
-
-        private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>();
-        private final Choreographer mChoreographer;
-        private boolean mCallbackScheduled;
-
-        public AnimationHelper() {
-            mChoreographer = Choreographer.getInstance();
-        }
-
-        public void addDelayedAnimation(RenderNodeAnimatorCompat animator) {
-            mDelayedAnims.add(animator);
-            scheduleCallback();
-        }
-
-        private void scheduleCallback() {
-            if (!mCallbackScheduled) {
-                mCallbackScheduled = true;
-                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
-            }
-        }
-
-        @Override
-        public void run() {
-            long frameTimeMs = mChoreographer.getFrameTime();
-            mCallbackScheduled = false;
-
-            int end = 0;
-            for (int i = 0; i < mDelayedAnims.size(); i++) {
-                RenderNodeAnimatorCompat animator = mDelayedAnims.get(i);
-                if (!animator.processDelayed(frameTimeMs)) {
-                    if (end != i) {
-                        mDelayedAnims.set(end, animator);
-                    }
-                    end++;
-                }
-            }
-            while (mDelayedAnims.size() > end) {
-                mDelayedAnims.remove(mDelayedAnims.size() - 1);
-            }
-
-            if (mDelayedAnims.size() > 0) {
-                scheduleCallback();
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index ea3efb1..eeff90a 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -30,6 +30,9 @@
      *
      * Any shadow cast by the View will respect the circular clip from this animator.
      *
+     * Note that the animation returned here is a one-shot animation. It cannot
+     * be re-used, and once started it cannot be paused or resumed.
+     *
      * @param view The View will be clipped to the animating circle.
      * @param centerX The x coordinate of the center of the animating circle.
      * @param centerY The y coordinate of the center of the animating circle.
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 20f5182..de96887 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -81,7 +81,7 @@
             int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
 
             final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue);
+            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
             animator.setStartDelay(startDelay);
             animator.setDuration(duration);
             animator.setInterpolator(interpolator);