if (mState != STATE_PREPARE) {
throw new IllegalStateException("Animator has already started, cannot change it now!");
}
+ if (mNativePtr == null) {
+ throw new IllegalStateException("Animator's target has been destroyed "
+ + "(trying to modify an animation after activity destroy?)");
+ }
}
static boolean isNativeInterpolator(TimeInterpolator interpolator) {
mState = STATE_DELAYED;
applyInterpolator();
- if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+ if (mNativePtr == null) {
+ // It's dead, immediately cancel
+ cancel();
+ } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
nSetStartDelay(mNativePtr.get(), mStartDelay);
doStart();
} else {
private void moveToRunningState() {
mState = STATE_RUNNING;
- nStart(mNativePtr.get(), this);
+ if (mNativePtr != null) {
+ nStart(mNativePtr.get());
+ }
notifyStartListeners();
}
getHelper().removeDelayedAnimation(this);
moveToRunningState();
}
- nEnd(mNativePtr.get());
final ArrayList<AnimatorListener> listeners = cloneListeners();
final int numListeners = listeners == null ? 0 : listeners.size();
listeners.get(i).onAnimationCancel(this);
}
- if (mViewTarget != null) {
- // Kick off a frame to flush the state change
- mViewTarget.invalidateViewProperty(true, false);
- }
+ end();
}
}
getHelper().removeDelayedAnimation(this);
doStart();
}
- nEnd(mNativePtr.get());
- if (mViewTarget != null) {
- // Kick off a frame to flush the state change
- mViewTarget.invalidateViewProperty(true, false);
+ if (mNativePtr != null) {
+ nEnd(mNativePtr.get());
+ if (mViewTarget != null) {
+ // Kick off a frame to flush the state change
+ mViewTarget.invalidateViewProperty(true, false);
+ }
+ } else {
+ // It's already dead, jump to onFinish
+ onFinished();
}
}
}
}
private void setTarget(RenderNode node) {
+ checkMutable();
if (mTarget != null) {
throw new IllegalStateException("Target already set!");
}
+ nSetListener(mNativePtr.get(), this);
mTarget = node;
mTarget.addAnimator(this);
}
}
protected void onFinished() {
+ if (mState == STATE_PREPARE) {
+ // Unlikely but possible, the native side has been destroyed
+ // before we have started.
+ releaseNativePtr();
+ return;
+ }
if (mState == STATE_DELAYED) {
getHelper().removeDelayedAnimation(this);
notifyStartListeners();
// Release the native object, as it has a global reference to us. This
// breaks the cyclic reference chain, and allows this object to be
// GC'd
- mNativePtr.release();
- mNativePtr = null;
+ releaseNativePtr();
+ }
+
+ private void releaseNativePtr() {
+ if (mNativePtr != null) {
+ mNativePtr.release();
+ mNativePtr = null;
+ }
}
@SuppressWarnings("unchecked")
private static native void nSetStartDelay(long nativePtr, long startDelay);
private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+ private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
- private static native void nStart(long animPtr, RenderNodeAnimator finishListener);
+ private static native void nStart(long animPtr);
private static native void nEnd(long animPtr);
}
animator->setAllowRunningAsync(mayRunAsync);
}
-static void start(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
+static void setListener(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
animator->setListener(new AnimationListenerBridge(env, finishListener));
+}
+
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+ BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
animator->start();
}
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
{ "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
{ "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
- { "nStart", "(JLandroid/view/RenderNodeAnimator;)V", (void*) start },
+ { "nSetListener", "(JLandroid/view/RenderNodeAnimator;)V", (void*) setListener},
+ { "nStart", "(J)V", (void*) start},
{ "nEnd", "(J)V", (void*) end },
#endif
};
package com.android.test.hwui;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewAnimationUtils;
private boolean mShouldBlock;
private int mIteration = 0;
+ private AnimatorListener mListener = new AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ Log.d("Reveal", "onAnimatorStart " + animation);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ Log.d("Reveal", "onAnimationRepeat " + animation);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Log.d("Reveal", "onAnimationEnd " + animation);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ Log.d("Reveal", "onAnimationCancel " + animation);
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Animator animator = ViewAnimationUtils.createCircularReveal(view,
view.getWidth() / 2, view.getHeight() / 2,
0, Math.max(view.getWidth(), view.getHeight()));
+ Log.d("Reveal", "Calling start...");
+ animator.addListener(mListener);
if (mIteration < 2) {
animator.setDuration(DURATION);
animator.start();
AnimatorSet set = new AnimatorSet();
set.playTogether(animator);
set.setDuration(DURATION);
+ set.addListener(mListener);
set.start();
}