summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/animation/RevealAnimator.java231
-rw-r--r--core/java/android/animation/ValueAnimator.java43
-rw-r--r--core/java/android/view/RenderNodeAnimator.java21
-rw-r--r--core/java/android/view/RenderNodeAnimatorCompat.java7
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewAnimationUtils.java3
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);
}
}