Merge "Fix behavior change for animators without a start delay"
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 16f825d..d444638 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -603,7 +603,17 @@
createDependencyGraph();
// Now that all dependencies are set up, start the animations that should be started.
- start(mRootNode);
+ boolean setIsEmpty = false;
+ if (mStartDelay > 0) {
+ start(mRootNode);
+ } else if (mNodes.size() > 1) {
+ // No delay, but there are other animators in the set
+ onChildAnimatorEnded(mDelayAnim);
+ } else {
+ // Set is empty, no delay, no other animation. Skip to end in this case
+ setIsEmpty = true;
+ }
+
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
@@ -612,18 +622,9 @@
tmpListeners.get(i).onAnimationStart(this);
}
}
- if (mNodes.size() == 0 && mStartDelay == 0) {
- // Handle unusual case where empty AnimatorSet is started - should send out
- // end event immediately since the event will not be sent out at all otherwise
- mStarted = false;
- if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(this);
- }
- }
+ if (setIsEmpty) {
+ // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
+ onChildAnimatorEnded(mDelayAnim);
}
}
@@ -751,44 +752,7 @@
public void onAnimationEnd(Animator animation) {
animation.removeListener(this);
mAnimatorSet.mPlayingSet.remove(animation);
- Node animNode = mAnimatorSet.mNodeMap.get(animation);
- animNode.mEnded = true;
-
- if (!mAnimatorSet.mTerminated) {
- List<Node> children = animNode.mChildNodes;
- // Start children animations, if any.
- int childrenSize = children == null ? 0 : children.size();
- for (int i = 0; i < childrenSize; i++) {
- if (children.get(i).mLatestParent == animNode) {
- mAnimatorSet.start(children.get(i));
- }
- }
- // Listeners are already notified of the AnimatorSet ending in cancel() or
- // end(); the logic below only kicks in when animations end normally
- boolean allDone = true;
- // Traverse the tree and find if there's any unfinished node
- int size = mAnimatorSet.mNodes.size();
- for (int i = 0; i < size; i++) {
- if (!mAnimatorSet.mNodes.get(i).mEnded) {
- allDone = false;
- break;
- }
- }
- if (allDone) {
- // If this was the last child animation to end, then notify listeners that this
- // AnimatorSet has ended
- if (mAnimatorSet.mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mAnimatorSet.mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
- }
- }
- mAnimatorSet.mStarted = false;
- mAnimatorSet.mPaused = false;
- }
- }
+ mAnimatorSet.onChildAnimatorEnded(animation);
}
// Nothing to do
@@ -801,6 +765,47 @@
}
+ private void onChildAnimatorEnded(Animator animation) {
+ Node animNode = mNodeMap.get(animation);
+ animNode.mEnded = true;
+
+ if (!mTerminated) {
+ List<Node> children = animNode.mChildNodes;
+ // Start children animations, if any.
+ int childrenSize = children == null ? 0 : children.size();
+ for (int i = 0; i < childrenSize; i++) {
+ if (children.get(i).mLatestParent == animNode) {
+ start(children.get(i));
+ }
+ }
+ // Listeners are already notified of the AnimatorSet ending in cancel() or
+ // end(); the logic below only kicks in when animations end normally
+ boolean allDone = true;
+ // Traverse the tree and find if there's any unfinished node
+ int size = mNodes.size();
+ for (int i = 0; i < size; i++) {
+ if (!mNodes.get(i).mEnded) {
+ allDone = false;
+ break;
+ }
+ }
+ if (allDone) {
+ // If this was the last child animation to end, then notify listeners that this
+ // AnimatorSet has ended
+ if (mListeners != null) {
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
+ }
+ mStarted = false;
+ mPaused = false;
+ }
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 5af6504..1995ef5 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -932,6 +932,13 @@
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, mStartDelay);
+
+ if (mStartDelay == 0) {
+ // If there's no start delay, init the animation and notify start listeners right away
+ // Otherwise, postpone this until the first frame after the start delay.
+ startAnimation();
+ setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+ }
}
@Override
@@ -1075,6 +1082,7 @@
mStartListenersCalled = false;
mPlayingBackwards = false;
mReversing = false;
+ mLastFrameTime = 0;
mCurrentIteration = 0;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1184,12 +1192,13 @@
* @hide
*/
public final void doAnimationFrame(long frameTime) {
- mLastFrameTime = frameTime;
AnimationHandler handler = AnimationHandler.getInstance();
- if (!mRunning) {
+ if (mLastFrameTime == 0) {
// First frame
handler.addOneShotCommitCallback(this);
- startAnimation();
+ if (mStartDelay > 0) {
+ startAnimation();
+ }
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
@@ -1199,6 +1208,7 @@
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
+ mLastFrameTime = frameTime;
if (mPaused) {
if (mPauseTime < 0) {
mPauseTime = frameTime;