Animator start and stop notifications during seek
Bug: 259287604 Seeking an AnimatorSet with setCurrentPlayTime will notify onAnimationStart() and onAnimationEnd() for itself and children, regardless of whether the AnimatorSet has been started or not. Test: new CTS tests Change-Id: Ibb83f613184f64381c4629cad1858b3aab8bf90a
This commit is contained in:
@@ -547,7 +547,6 @@ public abstract class Animator implements Cloneable {
|
||||
*/
|
||||
void skipToEndValue(boolean inReverse) {}
|
||||
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
@@ -565,13 +564,13 @@ public abstract class Animator implements Cloneable {
|
||||
* repetition. lastPlayTime is similar and is used to calculate how many repeats have been
|
||||
* done between the two times.
|
||||
*/
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime) {}
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {}
|
||||
|
||||
/**
|
||||
* Internal use only. This animates any animation that has ended since lastPlayTime.
|
||||
* If an animation hasn't been finished, no change will be made.
|
||||
*/
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {}
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {}
|
||||
|
||||
/**
|
||||
* Internal use only. Adds all start times (after delay) to and end times to times.
|
||||
|
||||
@@ -188,6 +188,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
*/
|
||||
private long[] mChildStartAndStopTimes;
|
||||
|
||||
/**
|
||||
* Tracks whether we've notified listeners of the onAnimationStart() event.
|
||||
*/
|
||||
private boolean mStartListenersCalled;
|
||||
|
||||
// This is to work around a bug in b/34736819. This needs to be removed once app team
|
||||
// fixes their side.
|
||||
private AnimatorListenerAdapter mAnimationEndListener = new AnimatorListenerAdapter() {
|
||||
@@ -736,14 +741,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
startAnimation();
|
||||
}
|
||||
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (int i = 0; i < numListeners; ++i) {
|
||||
tmpListeners.get(i).onAnimationStart(this, inReverse);
|
||||
}
|
||||
}
|
||||
notifyStartListeners(inReverse);
|
||||
if (isEmptySet) {
|
||||
// In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
|
||||
// onAnimationEnd() right away.
|
||||
@@ -751,6 +749,32 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStartListeners(boolean inReverse) {
|
||||
if (mListeners != null && !mStartListenersCalled) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (int i = 0; i < numListeners; ++i) {
|
||||
AnimatorListener listener = tmpListeners.get(i);
|
||||
listener.onAnimationStart(this, inReverse);
|
||||
}
|
||||
}
|
||||
mStartListenersCalled = true;
|
||||
}
|
||||
|
||||
private void notifyEndListeners(boolean inReverse) {
|
||||
if (mListeners != null && mStartListenersCalled) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (int i = 0; i < numListeners; ++i) {
|
||||
AnimatorListener listener = tmpListeners.get(i);
|
||||
listener.onAnimationEnd(this, inReverse);
|
||||
}
|
||||
}
|
||||
mStartListenersCalled = false;
|
||||
}
|
||||
|
||||
// Returns true if set is empty or contains nothing but animator sets with no start delay.
|
||||
private static boolean isEmptySet(AnimatorSet set) {
|
||||
if (set.getStartDelay() > 0) {
|
||||
@@ -823,7 +847,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
private void animateBasedOnPlayTime(
|
||||
long currentPlayTime,
|
||||
long lastPlayTime,
|
||||
boolean inReverse
|
||||
boolean inReverse,
|
||||
boolean notify
|
||||
) {
|
||||
if (currentPlayTime < 0 || lastPlayTime < -1) {
|
||||
throw new UnsupportedOperationException("Error: Play time should never be negative.");
|
||||
@@ -854,8 +879,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
while (index < endIndex) {
|
||||
long playTime = startEndTimes[index];
|
||||
if (lastPlayTime != playTime) {
|
||||
animateSkipToEnds(playTime, lastPlayTime);
|
||||
animateValuesInRange(playTime, lastPlayTime);
|
||||
animateSkipToEnds(playTime, lastPlayTime, notify);
|
||||
animateValuesInRange(playTime, lastPlayTime, notify);
|
||||
lastPlayTime = playTime;
|
||||
}
|
||||
index++;
|
||||
@@ -865,15 +890,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
index--;
|
||||
long playTime = startEndTimes[index];
|
||||
if (lastPlayTime != playTime) {
|
||||
animateSkipToEnds(playTime, lastPlayTime);
|
||||
animateValuesInRange(playTime, lastPlayTime);
|
||||
animateSkipToEnds(playTime, lastPlayTime, notify);
|
||||
animateValuesInRange(playTime, lastPlayTime, notify);
|
||||
lastPlayTime = playTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentPlayTime != lastPlayTime) {
|
||||
animateSkipToEnds(currentPlayTime, lastPlayTime);
|
||||
animateValuesInRange(currentPlayTime, lastPlayTime);
|
||||
animateSkipToEnds(currentPlayTime, lastPlayTime, notify);
|
||||
animateValuesInRange(currentPlayTime, lastPlayTime, notify);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,10 +918,13 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
}
|
||||
|
||||
@Override
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {
|
||||
initAnimation();
|
||||
|
||||
if (lastPlayTime > currentPlayTime) {
|
||||
if (notify) {
|
||||
notifyStartListeners(true);
|
||||
}
|
||||
for (int i = mEvents.size() - 1; i >= 0; i--) {
|
||||
AnimationEvent event = mEvents.get(i);
|
||||
Node node = event.mNode;
|
||||
@@ -904,23 +932,31 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
&& node.mStartTime != DURATION_INFINITE
|
||||
) {
|
||||
Animator animator = node.mAnimation;
|
||||
long start = node.mStartTime + animator.getStartDelay();
|
||||
long start = node.mStartTime;
|
||||
long end = node.mTotalDuration == DURATION_INFINITE
|
||||
? Long.MAX_VALUE : node.mEndTime;
|
||||
if (currentPlayTime <= start && start < lastPlayTime) {
|
||||
animator.animateSkipToEnds(
|
||||
start - node.mStartTime,
|
||||
lastPlayTime - node.mStartTime
|
||||
0,
|
||||
lastPlayTime - node.mStartTime,
|
||||
notify
|
||||
);
|
||||
} else if (start <= currentPlayTime && currentPlayTime <= end) {
|
||||
animator.animateSkipToEnds(
|
||||
currentPlayTime - node.mStartTime,
|
||||
lastPlayTime - node.mStartTime
|
||||
lastPlayTime - node.mStartTime,
|
||||
notify
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentPlayTime <= 0 && notify) {
|
||||
notifyEndListeners(true);
|
||||
}
|
||||
} else {
|
||||
if (notify) {
|
||||
notifyStartListeners(false);
|
||||
}
|
||||
int eventsSize = mEvents.size();
|
||||
for (int i = 0; i < eventsSize; i++) {
|
||||
AnimationEvent event = mEvents.get(i);
|
||||
@@ -929,29 +965,48 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
&& node.mStartTime != DURATION_INFINITE
|
||||
) {
|
||||
Animator animator = node.mAnimation;
|
||||
long start = node.mStartTime + animator.getStartDelay();
|
||||
long start = node.mStartTime;
|
||||
long end = node.mTotalDuration == DURATION_INFINITE
|
||||
? Long.MAX_VALUE : node.mEndTime;
|
||||
if (lastPlayTime < end && end <= currentPlayTime) {
|
||||
animator.animateSkipToEnds(
|
||||
end - node.mStartTime,
|
||||
lastPlayTime - node.mStartTime
|
||||
lastPlayTime - node.mStartTime,
|
||||
notify
|
||||
);
|
||||
} else if (start <= currentPlayTime && currentPlayTime <= end) {
|
||||
animator.animateSkipToEnds(
|
||||
currentPlayTime - node.mStartTime,
|
||||
lastPlayTime - node.mStartTime
|
||||
lastPlayTime - node.mStartTime,
|
||||
notify
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentPlayTime >= getTotalDuration() && notify) {
|
||||
notifyEndListeners(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime) {
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {
|
||||
initAnimation();
|
||||
|
||||
if (notify) {
|
||||
if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
|
||||
notifyStartListeners(false);
|
||||
} else {
|
||||
long duration = getTotalDuration();
|
||||
if (duration >= 0
|
||||
&& (lastPlayTime > duration || (lastPlayTime == duration
|
||||
&& currentPlayTime < duration))
|
||||
) {
|
||||
notifyStartListeners(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int eventsSize = mEvents.size();
|
||||
for (int i = 0; i < eventsSize; i++) {
|
||||
AnimationEvent event = mEvents.get(i);
|
||||
@@ -960,13 +1015,17 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
&& node.mStartTime != DURATION_INFINITE
|
||||
) {
|
||||
Animator animator = node.mAnimation;
|
||||
long start = node.mStartTime + animator.getStartDelay();
|
||||
long start = node.mStartTime;
|
||||
long end = node.mTotalDuration == DURATION_INFINITE
|
||||
? Long.MAX_VALUE : node.mEndTime;
|
||||
if (start < currentPlayTime && currentPlayTime < end) {
|
||||
if ((start < currentPlayTime && currentPlayTime < end)
|
||||
|| (start == currentPlayTime && lastPlayTime < start)
|
||||
|| (end == currentPlayTime && lastPlayTime > end)
|
||||
) {
|
||||
animator.animateValuesInRange(
|
||||
currentPlayTime - node.mStartTime,
|
||||
Math.max(-1, lastPlayTime - node.mStartTime)
|
||||
Math.max(-1, lastPlayTime - node.mStartTime),
|
||||
notify
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1021,6 +1080,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
* set to this time; it will simply set the time to this value and perform any appropriate
|
||||
* actions based on that time. If the animation is already running, then setCurrentPlayTime()
|
||||
* will set the current playing time to this value and continue playing from that point.
|
||||
* On {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, an AnimatorSet
|
||||
* that hasn't been {@link #start()}ed, will issue
|
||||
* {@link android.animation.Animator.AnimatorListener#onAnimationStart(Animator, boolean)}
|
||||
* and {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator, boolean)}
|
||||
* events.
|
||||
*
|
||||
* @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
|
||||
* Unless the animation is reversing, the playtime is considered the time since
|
||||
@@ -1042,12 +1106,12 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
|
||||
initAnimation();
|
||||
|
||||
long lastPlayTime = mSeekState.getPlayTime();
|
||||
if (!isStarted() || isPaused()) {
|
||||
if (mReversing && !isStarted()) {
|
||||
throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
|
||||
+ " should not be set when AnimatorSet is not started.");
|
||||
}
|
||||
long lastPlayTime = mSeekState.getPlayTime();
|
||||
if (!mSeekState.isActive()) {
|
||||
findLatestEventIdForTime(0);
|
||||
initChildren();
|
||||
@@ -1055,13 +1119,9 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
skipToEndValue(!mReversing);
|
||||
mSeekState.setPlayTime(0, mReversing);
|
||||
}
|
||||
animateBasedOnPlayTime(playTime, lastPlayTime, mReversing);
|
||||
mSeekState.setPlayTime(playTime, mReversing);
|
||||
} else {
|
||||
// If the animation is running, just set the seek time and wait until the next frame
|
||||
// (i.e. doAnimationFrame(...)) to advance the animation.
|
||||
mSeekState.setPlayTime(playTime, mReversing);
|
||||
}
|
||||
animateBasedOnPlayTime(playTime, lastPlayTime, mReversing, true);
|
||||
mSeekState.setPlayTime(playTime, mReversing);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1101,7 +1161,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
|
||||
long previousTime = -1;
|
||||
for (long time : times) {
|
||||
animateBasedOnPlayTime(time, previousTime, false);
|
||||
animateBasedOnPlayTime(time, previousTime, false, false);
|
||||
previousTime = time;
|
||||
}
|
||||
}
|
||||
@@ -1397,15 +1457,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
|
||||
|
||||
// No longer receive callbacks
|
||||
removeAnimationCallback();
|
||||
// Call end listener
|
||||
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, mReversing);
|
||||
}
|
||||
}
|
||||
notifyEndListeners(mReversing);
|
||||
removeAnimationEndListener();
|
||||
mSelfPulse = true;
|
||||
mReversing = false;
|
||||
|
||||
@@ -1108,18 +1108,30 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyStartListeners() {
|
||||
private void notifyStartListeners(boolean isReversing) {
|
||||
if (mListeners != null && !mStartListenersCalled) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (int i = 0; i < numListeners; ++i) {
|
||||
tmpListeners.get(i).onAnimationStart(this, mReversing);
|
||||
tmpListeners.get(i).onAnimationStart(this, isReversing);
|
||||
}
|
||||
}
|
||||
mStartListenersCalled = true;
|
||||
}
|
||||
|
||||
private void notifyEndListeners(boolean isReversing) {
|
||||
if (mListeners != null && mStartListenersCalled) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (int i = 0; i < numListeners; ++i) {
|
||||
tmpListeners.get(i).onAnimationEnd(this, isReversing);
|
||||
}
|
||||
}
|
||||
mStartListenersCalled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the animation playing. This version of start() takes a boolean flag that indicates
|
||||
* whether the animation should play in reverse. The flag is usually false, but may be set
|
||||
@@ -1210,7 +1222,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
if ((mStarted || mRunning) && mListeners != null) {
|
||||
if (!mRunning) {
|
||||
// If it's not yet running, then start listeners weren't called. Call them now.
|
||||
notifyStartListeners();
|
||||
notifyStartListeners(mReversing);
|
||||
}
|
||||
int listenersSize = mListeners.size();
|
||||
if (listenersSize > 0) {
|
||||
@@ -1324,22 +1336,14 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
boolean notify = (mStarted || mRunning) && mListeners != null;
|
||||
if (notify && !mRunning) {
|
||||
// If it's not yet running, then start listeners weren't called. Call them now.
|
||||
notifyStartListeners();
|
||||
notifyStartListeners(mReversing);
|
||||
}
|
||||
mRunning = false;
|
||||
mStarted = false;
|
||||
mStartListenersCalled = false;
|
||||
mLastFrameTime = -1;
|
||||
mFirstFrameTime = -1;
|
||||
mStartTime = -1;
|
||||
if (notify && 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, mReversing);
|
||||
}
|
||||
}
|
||||
notifyEndListeners(mReversing);
|
||||
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
|
||||
mReversing = false;
|
||||
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
|
||||
@@ -1366,9 +1370,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
} else {
|
||||
mOverallFraction = 0f;
|
||||
}
|
||||
if (mListeners != null) {
|
||||
notifyStartListeners();
|
||||
}
|
||||
|
||||
notifyStartListeners(mReversing);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1459,13 +1462,22 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
* will be called.
|
||||
*/
|
||||
@Override
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime) {
|
||||
if (currentPlayTime < mStartDelay || lastPlayTime < -1) {
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {
|
||||
if (currentPlayTime < 0 || lastPlayTime < -1) {
|
||||
throw new UnsupportedOperationException("Error: Play time should never be negative.");
|
||||
}
|
||||
|
||||
initAnimation();
|
||||
long duration = getTotalDuration();
|
||||
if (notify) {
|
||||
if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) {
|
||||
notifyStartListeners(false);
|
||||
} else if (lastPlayTime > duration
|
||||
|| (lastPlayTime == duration && currentPlayTime < duration)
|
||||
) {
|
||||
notifyStartListeners(true);
|
||||
}
|
||||
}
|
||||
if (duration >= 0) {
|
||||
lastPlayTime = Math.min(duration, lastPlayTime);
|
||||
}
|
||||
@@ -1474,8 +1486,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
|
||||
// Check whether repeat callback is needed only when repeat count is non-zero
|
||||
if (mRepeatCount > 0) {
|
||||
int iteration = (int) (currentPlayTime / mDuration);
|
||||
int lastIteration = (int) (lastPlayTime / mDuration);
|
||||
int iteration = Math.max(0, (int) (currentPlayTime / mDuration));
|
||||
int lastIteration = Math.max(0, (int) (lastPlayTime / mDuration));
|
||||
|
||||
// Clamp iteration to [0, mRepeatCount]
|
||||
iteration = Math.min(iteration, mRepeatCount);
|
||||
@@ -1491,24 +1503,33 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
|
||||
}
|
||||
}
|
||||
|
||||
if (mRepeatCount != INFINITE && currentPlayTime >= (mRepeatCount + 1) * mDuration) {
|
||||
if (mRepeatCount != INFINITE && currentPlayTime > (mRepeatCount + 1) * mDuration) {
|
||||
throw new IllegalStateException("Can't animate a value outside of the duration");
|
||||
} else {
|
||||
// Find the current fraction:
|
||||
float fraction = currentPlayTime / (float) mDuration;
|
||||
float fraction = Math.max(0, currentPlayTime) / (float) mDuration;
|
||||
fraction = getCurrentIterationFraction(fraction, false);
|
||||
animateValue(fraction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {
|
||||
if (currentPlayTime <= mStartDelay && lastPlayTime > mStartDelay) {
|
||||
skipToEndValue(true);
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {
|
||||
boolean inReverse = currentPlayTime < lastPlayTime;
|
||||
boolean doSkip;
|
||||
if (currentPlayTime <= 0 && lastPlayTime > 0) {
|
||||
doSkip = true;
|
||||
} else {
|
||||
long duration = getTotalDuration();
|
||||
if (duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration) {
|
||||
skipToEndValue(false);
|
||||
doSkip = duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration;
|
||||
}
|
||||
if (doSkip) {
|
||||
if (notify) {
|
||||
notifyStartListeners(inReverse);
|
||||
}
|
||||
skipToEndValue(inReverse);
|
||||
if (notify) {
|
||||
notifyEndListeners(inReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user