diff options
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/animation/RevealAnimator.java | 231 | ||||
-rw-r--r-- | core/java/android/animation/ValueAnimator.java | 43 | ||||
-rw-r--r-- | core/java/android/view/RenderNodeAnimator.java | 21 | ||||
-rw-r--r-- | core/java/android/view/RenderNodeAnimatorCompat.java | 7 | ||||
-rw-r--r-- | core/java/android/view/View.java | 6 | ||||
-rw-r--r-- | core/java/android/view/ViewAnimationUtils.java | 3 |
6 files changed, 216 insertions, 95 deletions
diff --git a/core/java/android/animation/RevealAnimator.java b/core/java/android/animation/RevealAnimator.java index 77a536a..a1cbd45 100644 --- a/core/java/android/animation/RevealAnimator.java +++ b/core/java/android/animation/RevealAnimator.java @@ -16,10 +16,9 @@ package android.animation; +import android.view.RenderNodeAnimator; import android.view.View; -import java.util.ArrayList; - /** * Reveals a View with an animated clipping circle. * The clipping is implemented efficiently by talking to a private reveal API on View. @@ -28,114 +27,178 @@ import java.util.ArrayList; * @hide */ public class RevealAnimator extends ValueAnimator { - private final static String LOGTAG = "RevealAnimator"; - private ValueAnimator.AnimatorListener mListener; - private ValueAnimator.AnimatorUpdateListener mUpdateListener; - private RevealCircle mReuseRevealCircle = new RevealCircle(0); - private RevealAnimator(final View clipView, final int x, final int y, - float startRadius, float endRadius, final boolean inverseClip) { - - setObjectValues(new RevealCircle(startRadius), new RevealCircle(endRadius)); - setEvaluator(new RevealCircleEvaluator(mReuseRevealCircle)); - - mUpdateListener = new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - RevealCircle circle = (RevealCircle) animation.getAnimatedValue(); - float radius = circle.getRadius(); - clipView.setRevealClip(true, inverseClip, x, y, radius); - } - }; - mListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - clipView.setRevealClip(false, false, 0, 0, 0); - } - - @Override - public void onAnimationEnd(Animator animation) { - clipView.setRevealClip(false, false, 0, 0, 0); - } - }; - addUpdateListener(mUpdateListener); - addListener(mListener); - } - - public static RevealAnimator ofRevealCircle(View clipView, int x, int y, + + private View mClipView; + private int mX, mY; + private boolean mInverseClip; + 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, boolean inverseClip) { - RevealAnimator anim = new RevealAnimator(clipView, x, y, - startRadius, endRadius, inverseClip); - return anim; + mClipView = clipView; + mStartRadius = startRadius; + mEndRadius = endRadius; + mDelta = endRadius - startRadius; + mX = x; + mY = y; + mInverseClip = inverseClip; + super.setValues(PropertyValuesHolder.ofFloat("radius", startRadius, endRadius)); } - - /** - * {@inheritDoc} - */ @Override - public void removeAllUpdateListeners() { - super.removeAllUpdateListeners(); - addUpdateListener(mUpdateListener); + void animateValue(float fraction) { + super.animateValue(fraction); + fraction = getAnimatedFraction(); + float radius = mStartRadius + (mDelta * fraction); + mClipView.setRevealClip(true, mInverseClip, mX, mY, radius); } - /** - * {@inheritDoc} - */ @Override - public void removeAllListeners() { - super.removeAllListeners(); - addListener(mListener); + protected void endAnimation(AnimationHandler handler) { + mClipView.setRevealClip(false, false, 0, 0, 0); + super.endAnimation(handler); } - /** - * {@inheritDoc} - */ @Override - public ArrayList<AnimatorListener> getListeners() { - ArrayList<AnimatorListener> allListeners = - (ArrayList<AnimatorListener>) super.getListeners().clone(); - allListeners.remove(mListener); - return allListeners; + public void setAllowRunningAsynchronously(boolean mayRunAsync) { + mMayRunAsync = mayRunAsync; } - private class RevealCircle { - float mRadius; + 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; + } - public RevealCircle(float radius) { - mRadius = radius; + @Override + public void start() { + if (mRtAnimator != null) { + mRtAnimator.end(); + mRtAnimator = null; + } + if (canRunAsync()) { + mRtAnimator = new RenderNodeAnimator(mX, mY, mInverseClip, mStartRadius, mEndRadius); + mRtAnimator.setDuration(getDuration()); + mRtAnimator.setInterpolator(getInterpolator()); + mRtAnimator.setTarget(mClipView); + // TODO: Listeners + mRtAnimator.start(); + } else { + super.start(); } + } - public void setRadius(float radius) { - mRadius = radius; + @Override + public void cancel() { + if (mRtAnimator != null) { + mRtAnimator.cancel(); + } else { + super.cancel(); } + } - public float getRadius() { - return mRadius; + @Override + public void end() { + if (mRtAnimator != null) { + mRtAnimator.end(); + } else { + super.end(); } } - private class RevealCircleEvaluator implements TypeEvaluator<RevealCircle> { + @Override + public void resume() { + if (mRtAnimator != null) { + // TODO: Support? Reject? + } else { + super.resume(); + } + } - private RevealCircle mRevealCircle; + @Override + public void pause() { + if (mRtAnimator != null) { + // TODO: see resume() + } else { + super.pause(); + } + } - public RevealCircleEvaluator() { + @Override + public boolean isRunning() { + if (mRtAnimator != null) { + return mRtAnimator.isRunning(); + } else { + return super.isRunning(); } + } - public RevealCircleEvaluator(RevealCircle reuseCircle) { - mRevealCircle = reuseCircle; + @Override + public boolean isStarted() { + if (mRtAnimator != null) { + return mRtAnimator.isStarted(); + } else { + return super.isStarted(); } + } - @Override - public RevealCircle evaluate(float fraction, RevealCircle startValue, - RevealCircle endValue) { - float currentRadius = startValue.mRadius - + ((endValue.mRadius - startValue.mRadius) * fraction); - if (mRevealCircle == null) { - return new RevealCircle(currentRadius); - } else { - mRevealCircle.setRadius(currentRadius); - return mRevealCircle; - } + @Override + public void reverse() { + if (mRtAnimator != null) { + // TODO support + } else { + super.reverse(); } } + + @Override + public ValueAnimator clone() { + RevealAnimator anim = (RevealAnimator) super.clone(); + anim.mRtAnimator = null; + return anim; + } + + // ---------------------------------------- + // All the things we don't allow + // ---------------------------------------- + + @Override + public void setValues(PropertyValuesHolder... values) { + throw new IllegalStateException("Cannot change the values of RevealAnimator"); + } + + @Override + public void setFloatValues(float... values) { + throw new IllegalStateException("Cannot change the values of RevealAnimator"); + } + + @Override + public void setIntValues(int... values) { + throw new IllegalStateException("Cannot change the values of RevealAnimator"); + } + + @Override + public void setObjectValues(Object... values) { + throw new IllegalStateException("Cannot change the values of RevealAnimator"); + } + + @Override + public void setEvaluator(TypeEvaluator value) { + throw new IllegalStateException("Cannot change the evaluator of RevealAnimator"); + } } diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index e3380a9..b13838a 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -206,7 +206,7 @@ public class ValueAnimator extends Animator { /** * The set of listeners to be sent events through the life of an animation. */ - private ArrayList<AnimatorUpdateListener> mUpdateListeners = null; + ArrayList<AnimatorUpdateListener> mUpdateListeners = null; /** * The property/value sets being animated. @@ -1064,8 +1064,9 @@ public class ValueAnimator extends Animator { /** * Called internally to end an animation by removing it from the animations list. Must be * called on the UI thread. + * @hide */ - private void endAnimation(AnimationHandler handler) { + protected void endAnimation(AnimationHandler handler) { handler.mAnimations.remove(this); handler.mPendingAnimations.remove(this); handler.mDelayedAnims.remove(this); @@ -1373,4 +1374,42 @@ public class ValueAnimator extends Animator { } return returnVal; } + + /** + * <p>Whether or not the ValueAnimator is allowed to run asynchronously off of + * the UI thread. This is a hint that informs the ValueAnimator that it is + * OK to run the animation off-thread, however ValueAnimator may decide + * that it must run the animation on the UI thread anyway. For example if there + * is an {@link AnimatorUpdateListener} the animation will run on the UI thread, + * regardless of the value of this hint.</p> + * + * <p>Regardless of whether or not the animation runs asynchronously, all + * listener callbacks will be called on the UI thread.</p> + * + * <p>To be able to use this hint the following must be true:</p> + * <ol> + * <li>{@link #getAnimatedFraction()} is not needed (it will return undefined values).</li> + * <li>The animator is immutable while {@link #isStarted()} is true. Requests + * to change values, duration, delay, etc... may be ignored.</li> + * <li>Lifecycle callback events may be asynchronous. Events such as + * {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or + * {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed + * as they must be posted back to the UI thread, and any actions performed + * by those callbacks (such as starting new animations) will not happen + * in the same frame.</li> + * <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...) + * may be asynchronous. It is guaranteed that all state changes that are + * performed on the UI thread in the same frame will be applied as a single + * atomic update, however that frame may be the current frame, + * the next frame, or some future frame. This will also impact the observed + * state of the Animator. For example, {@link #isStarted()} may still return true + * after a call to {@link #end()}. Using the lifecycle callbacks is preferred over + * queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()} + * for this reason.</li> + * </ol> + * @hide + */ + public void setAllowRunningAsynchronously(boolean mayRunAsync) { + // It is up to subclasses to support this, if they can. + } } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 4b53c8e..2a06336 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -125,6 +125,12 @@ public class RenderNodeAnimator extends Animator { property.getNativeContainer(), paintField, finalValue)); } + public RenderNodeAnimator(int x, int y, boolean inverseClip, + float startRadius, float endRadius) { + init(nCreateRevealAnimator(new WeakReference<>(this), + x, y, inverseClip, startRadius, endRadius)); + } + private void init(long ptr) { mNativePtr = new VirtualRefBasePtr(ptr); } @@ -190,7 +196,7 @@ public class RenderNodeAnimator extends Animator { @Override public void cancel() { if (!mFinished) { - nCancel(mNativePtr.get()); + nEnd(mNativePtr.get()); final ArrayList<AnimatorListener> listeners = getListeners(); final int numListeners = listeners == null ? 0 : listeners.size(); @@ -202,7 +208,9 @@ public class RenderNodeAnimator extends Animator { @Override public void end() { - throw new UnsupportedOperationException(); + if (!mFinished) { + nEnd(mNativePtr.get()); + } } @Override @@ -281,6 +289,11 @@ public class RenderNodeAnimator extends Animator { } @Override + public boolean isStarted() { + return mStarted; + } + + @Override public void setInterpolator(TimeInterpolator interpolator) { checkMutable(); mInterpolator = interpolator; @@ -319,6 +332,8 @@ public class RenderNodeAnimator extends Animator { long canvasProperty, float finalValue); private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, long canvasProperty, int paintField, float finalValue); + private static native long nCreateRevealAnimator(WeakReference<RenderNodeAnimator> weakThis, + int x, int y, boolean inverseClip, float startRadius, float endRadius); private static native void nSetStartValue(long nativePtr, float startValue); private static native void nSetDuration(long nativePtr, long duration); @@ -328,5 +343,5 @@ public class RenderNodeAnimator extends Animator { private static native void nSetInterpolator(long animPtr, long interpolatorPtr); private static native void nStart(long animPtr); - private static native void nCancel(long animPtr); + private static native void nEnd(long animPtr); } diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java index 151277a..8103f47 100644 --- a/core/java/android/view/RenderNodeAnimatorCompat.java +++ b/core/java/android/view/RenderNodeAnimatorCompat.java @@ -31,6 +31,7 @@ public class RenderNodeAnimatorCompat extends RenderNodeAnimator { private long mStartDelay = 0; private long mStartTime; private boolean mCanceled; + private boolean mStarted; public RenderNodeAnimatorCompat(int property, float finalValue) { super(property, finalValue); @@ -49,6 +50,7 @@ public class RenderNodeAnimatorCompat extends RenderNodeAnimator { @Override public void start() { + mStarted = true; if (mStartDelay <= 0) { doStart(); } else { @@ -56,6 +58,11 @@ public class RenderNodeAnimatorCompat extends RenderNodeAnimator { } } + @Override + public boolean isStarted() { + return mStarted; + } + private void doStart() { if (!mCanceled) { super.start(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 7bc8bc5..85fa1e6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10724,8 +10724,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public final ValueAnimator createClearCircleAnimator(int centerX, int centerY, float startRadius, float endRadius) { - return RevealAnimator.ofRevealCircle(this, centerX, centerY, - startRadius, endRadius, true); + return new RevealAnimator(this, centerX, centerY, startRadius, endRadius, true); } /** @@ -10867,8 +10866,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setRevealClip(boolean shouldClip, boolean inverseClip, float x, float y, float radius) { mRenderNode.setRevealClip(shouldClip, inverseClip, x, y, radius); - // TODO: Handle this invalidate in a better way, or purely in native. - invalidate(); + invalidateViewProperty(false, false); } /** diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java index 29e865f..bee35ae 100644 --- a/core/java/android/view/ViewAnimationUtils.java +++ b/core/java/android/view/ViewAnimationUtils.java @@ -38,7 +38,6 @@ public final class ViewAnimationUtils { */ public static final ValueAnimator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) { - return RevealAnimator.ofRevealCircle(view, centerX, centerY, - startRadius, endRadius, false); + return new RevealAnimator(view, centerX, centerY, startRadius, endRadius, false); } } |