Merge changes from topic "revert-20590670-animator_seeking-RVESPLGWYK"

* changes:
  Revert "Improve AnimatorSet seekability."
  Revert "Animator start and stop notifications during seek"
This commit is contained in:
TreeHugger Robot
2023-01-13 21:40:40 +00:00
committed by Android (Google) Code Review
3 changed files with 157 additions and 391 deletions

View File

@@ -23,7 +23,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config; import android.content.pm.ActivityInfo.Config;
import android.content.res.ConstantState; import android.content.res.ConstantState;
import android.os.Build; import android.os.Build;
import android.util.LongArray;
import java.util.ArrayList; import java.util.ArrayList;
@@ -547,6 +546,7 @@ public abstract class Animator implements Cloneable {
*/ */
void skipToEndValue(boolean inReverse) {} void skipToEndValue(boolean inReverse) {}
/** /**
* Internal use only. * Internal use only.
* *
@@ -559,36 +559,9 @@ public abstract class Animator implements Cloneable {
} }
/** /**
* Internal use only. Changes the value of the animator as if currentPlayTime has passed since * Internal use only.
* the start of the animation. Therefore, currentPlayTime includes the start delay, and any
* 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, boolean notify) {} void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {}
/**
* 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, boolean notify) {}
/**
* Internal use only. Adds all start times (after delay) to and end times to times.
* The value must include offset.
*/
void getStartAndEndTimes(LongArray times, long offset) {
long startTime = offset + getStartDelay();
if (times.indexOf(startTime) < 0) {
times.add(startTime);
}
long duration = getTotalDuration();
if (duration != DURATION_INFINITE) {
long endTime = duration + offset;
if (times.indexOf(endTime) < 0) {
times.add(endTime);
}
}
}
/** /**
* <p>An animation listener receives notifications from an animation. * <p>An animation listener receives notifications from an animation.

View File

@@ -23,11 +23,9 @@ import android.os.Looper;
import android.util.AndroidRuntimeException; import android.util.AndroidRuntimeException;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.LongArray;
import android.view.animation.Animation; import android.view.animation.Animation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@@ -183,16 +181,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
*/ */
private long mPauseTime = -1; private long mPauseTime = -1;
/**
* The start and stop times of all descendant animators.
*/
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 // This is to work around a bug in b/34736819. This needs to be removed once app team
// fixes their side. // fixes their side.
private AnimatorListenerAdapter mAnimationEndListener = new AnimatorListenerAdapter() { private AnimatorListenerAdapter mAnimationEndListener = new AnimatorListenerAdapter() {
@@ -741,7 +729,14 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
startAnimation(); startAnimation();
} }
notifyStartListeners(inReverse); 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);
}
}
if (isEmptySet) { if (isEmptySet) {
// In the case of empty AnimatorSet, or 0 duration scale, we will trigger the // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
// onAnimationEnd() right away. // onAnimationEnd() right away.
@@ -749,32 +744,6 @@ 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. // Returns true if set is empty or contains nothing but animator sets with no start delay.
private static boolean isEmptySet(AnimatorSet set) { private static boolean isEmptySet(AnimatorSet set) {
if (set.getStartDelay() > 0) { if (set.getStartDelay() > 0) {
@@ -810,25 +779,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
@Override @Override
void skipToEndValue(boolean inReverse) { void skipToEndValue(boolean inReverse) {
if (!isInitialized()) {
throw new UnsupportedOperationException("Children must be initialized.");
}
// This makes sure the animation events are sorted an up to date. // This makes sure the animation events are sorted an up to date.
initAnimation(); initAnimation();
initChildren();
// Calling skip to the end in the sequence that they would be called in a forward/reverse // Calling skip to the end in the sequence that they would be called in a forward/reverse
// run, such that the sequential animations modifying the same property would have // run, such that the sequential animations modifying the same property would have
// the right value in the end. // the right value in the end.
if (inReverse) { if (inReverse) {
for (int i = mEvents.size() - 1; i >= 0; i--) { for (int i = mEvents.size() - 1; i >= 0; i--) {
AnimationEvent event = mEvents.get(i); if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { mEvents.get(i).mNode.mAnimation.skipToEndValue(true);
event.mNode.mAnimation.skipToEndValue(true);
} }
} }
} else { } else {
for (int i = 0; i < mEvents.size(); i++) { for (int i = 0; i < mEvents.size(); i++) {
AnimationEvent event = mEvents.get(i); if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) {
if (event.mEvent == AnimationEvent.ANIMATION_END) { mEvents.get(i).mNode.mAnimation.skipToEndValue(false);
event.mNode.mAnimation.skipToEndValue(false);
} }
} }
} }
@@ -844,216 +814,72 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
* {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)}, * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)},
* as needed, based on the last play time and current play time. * as needed, based on the last play time and current play time.
*/ */
private void animateBasedOnPlayTime( @Override
long currentPlayTime, void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
long lastPlayTime, if (currentPlayTime < 0 || lastPlayTime < 0) {
boolean inReverse,
boolean notify
) {
if (currentPlayTime < 0 || lastPlayTime < -1) {
throw new UnsupportedOperationException("Error: Play time should never be negative."); throw new UnsupportedOperationException("Error: Play time should never be negative.");
} }
// TODO: take into account repeat counts and repeat callback when repeat is implemented. // TODO: take into account repeat counts and repeat callback when repeat is implemented.
// Clamp currentPlayTime and lastPlayTime
// TODO: Make this more efficient
// Convert the play times to the forward direction.
if (inReverse) { if (inReverse) {
long duration = getTotalDuration(); if (getTotalDuration() == DURATION_INFINITE) {
if (duration == DURATION_INFINITE) { throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite"
throw new UnsupportedOperationException( + " duration");
"Cannot reverse AnimatorSet with infinite duration"
);
} }
// Convert the play times to the forward direction. long duration = getTotalDuration() - mStartDelay;
currentPlayTime = Math.min(currentPlayTime, duration); currentPlayTime = Math.min(currentPlayTime, duration);
currentPlayTime = duration - currentPlayTime; currentPlayTime = duration - currentPlayTime;
lastPlayTime = duration - lastPlayTime; lastPlayTime = duration - lastPlayTime;
inReverse = false;
} }
long[] startEndTimes = ensureChildStartAndEndTimes(); ArrayList<Node> unfinishedNodes = new ArrayList<>();
int index = findNextIndex(lastPlayTime, startEndTimes); // Assumes forward playing from here on.
int endIndex = findNextIndex(currentPlayTime, startEndTimes); for (int i = 0; i < mEvents.size(); i++) {
// Change values at the start/end times so that values are set in the right order.
// We don't want an animator that would finish before another to override the value
// set by another animator that finishes earlier.
if (currentPlayTime >= lastPlayTime) {
while (index < endIndex) {
long playTime = startEndTimes[index];
if (lastPlayTime != playTime) {
animateSkipToEnds(playTime, lastPlayTime, notify);
animateValuesInRange(playTime, lastPlayTime, notify);
lastPlayTime = playTime;
}
index++;
}
} else {
while (index > endIndex) {
index--;
long playTime = startEndTimes[index];
if (lastPlayTime != playTime) {
animateSkipToEnds(playTime, lastPlayTime, notify);
animateValuesInRange(playTime, lastPlayTime, notify);
lastPlayTime = playTime;
}
}
}
if (currentPlayTime != lastPlayTime) {
animateSkipToEnds(currentPlayTime, lastPlayTime, notify);
animateValuesInRange(currentPlayTime, lastPlayTime, notify);
}
}
/**
* Looks through startEndTimes for playTime. If it is in startEndTimes, the index after
* is returned. Otherwise, it returns the index at which it would be placed if it were
* to be inserted.
*/
private int findNextIndex(long playTime, long[] startEndTimes) {
int index = Arrays.binarySearch(startEndTimes, playTime);
if (index < 0) {
index = -index - 1;
} else {
index++;
}
return index;
}
@Override
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;
if (event.mEvent == AnimationEvent.ANIMATION_END
&& node.mStartTime != DURATION_INFINITE
) {
Animator animator = node.mAnimation;
long start = node.mStartTime;
long end = node.mTotalDuration == DURATION_INFINITE
? Long.MAX_VALUE : node.mEndTime;
if (currentPlayTime <= start && start < lastPlayTime) {
animator.animateSkipToEnds(
0,
lastPlayTime - node.mStartTime,
notify
);
} else if (start <= currentPlayTime && currentPlayTime <= end) {
animator.animateSkipToEnds(
currentPlayTime - 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);
Node node = event.mNode;
if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
&& node.mStartTime != DURATION_INFINITE
) {
Animator animator = node.mAnimation;
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,
notify
);
} else if (start <= currentPlayTime && currentPlayTime <= end) {
animator.animateSkipToEnds(
currentPlayTime - node.mStartTime,
lastPlayTime - node.mStartTime,
notify
);
}
}
}
if (currentPlayTime >= getTotalDuration() && notify) {
notifyEndListeners(false);
}
}
}
@Override
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); AnimationEvent event = mEvents.get(i);
Node node = event.mNode; if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED break;
&& node.mStartTime != DURATION_INFINITE }
) {
Animator animator = node.mAnimation; // This animation started prior to the current play time, and won't finish before the
long start = node.mStartTime; // play time, add to the unfinished list.
long end = node.mTotalDuration == DURATION_INFINITE if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
? Long.MAX_VALUE : node.mEndTime; if (event.mNode.mEndTime == DURATION_INFINITE
if ((start < currentPlayTime && currentPlayTime < end) || event.mNode.mEndTime > currentPlayTime) {
|| (start == currentPlayTime && lastPlayTime < start) unfinishedNodes.add(event.mNode);
|| (end == currentPlayTime && lastPlayTime > end)
) {
animator.animateValuesInRange(
currentPlayTime - node.mStartTime,
Math.max(-1, lastPlayTime - node.mStartTime),
notify
);
} }
} }
} // For animations that do finish before the play time, end them in the sequence that
} // they would in a normal run.
if (event.mEvent == AnimationEvent.ANIMATION_END) {
private long[] ensureChildStartAndEndTimes() { // Skip to the end of the animation.
if (mChildStartAndStopTimes == null) { event.mNode.mAnimation.skipToEndValue(false);
LongArray startAndEndTimes = new LongArray();
getStartAndEndTimes(startAndEndTimes, 0);
long[] times = startAndEndTimes.toArray();
Arrays.sort(times);
mChildStartAndStopTimes = times;
}
return mChildStartAndStopTimes;
}
@Override
void getStartAndEndTimes(LongArray times, long offset) {
int eventsSize = mEvents.size();
for (int i = 0; i < eventsSize; i++) {
AnimationEvent event = mEvents.get(i);
if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
&& event.mNode.mStartTime != DURATION_INFINITE
) {
event.mNode.mAnimation.getStartAndEndTimes(times, offset + event.mNode.mStartTime);
} }
} }
// Seek unfinished animation to the right time.
for (int i = 0; i < unfinishedNodes.size(); i++) {
Node node = unfinishedNodes.get(i);
long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
if (!inReverse) {
playTime -= node.mAnimation.getStartDelay();
}
node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
}
// Seek not yet started animations.
for (int i = 0; i < mEvents.size(); i++) {
AnimationEvent event = mEvents.get(i);
if (event.getTime() > currentPlayTime
&& event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
event.mNode.mAnimation.skipToEndValue(true);
}
}
} }
@Override @Override
@@ -1073,6 +899,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
return mChildrenInitialized; return mChildrenInitialized;
} }
private void skipToStartValue(boolean inReverse) {
skipToEndValue(!inReverse);
}
/** /**
* Sets the position of the animation to the specified point in time. This time should * Sets the position of the animation to the specified point in time. This time should
* be between 0 and the total duration of the animation, including any repetition. If * be between 0 and the total duration of the animation, including any repetition. If
@@ -1080,11 +910,6 @@ 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 * 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() * 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. * 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. * @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 * Unless the animation is reversing, the playtime is considered the time since
@@ -1101,27 +926,29 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay) if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
|| playTime < 0) { || playTime < 0) {
throw new UnsupportedOperationException("Error: Play time should always be in between" throw new UnsupportedOperationException("Error: Play time should always be in between"
+ " 0 and duration."); + "0 and duration.");
} }
initAnimation(); initAnimation();
long lastPlayTime = mSeekState.getPlayTime();
if (!isStarted() || isPaused()) { if (!isStarted() || isPaused()) {
if (mReversing && !isStarted()) { if (mReversing) {
throw new UnsupportedOperationException("Error: Something went wrong. mReversing" throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
+ " should not be set when AnimatorSet is not started."); + " should not be set when AnimatorSet is not started.");
} }
if (!mSeekState.isActive()) { if (!mSeekState.isActive()) {
findLatestEventIdForTime(0); findLatestEventIdForTime(0);
initChildren();
// Set all the values to start values. // Set all the values to start values.
skipToEndValue(!mReversing); initChildren();
mSeekState.setPlayTime(0, mReversing); mSeekState.setPlayTime(0, mReversing);
} }
animateBasedOnPlayTime(playTime, 0, 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);
} }
/** /**
@@ -1154,16 +981,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
private void initChildren() { private void initChildren() {
if (!isInitialized()) { if (!isInitialized()) {
mChildrenInitialized = true; mChildrenInitialized = true;
// Forcefully initialize all children based on their end time, so that if the start
// We have to initialize all the start values so that they are based on the previous // value of a child is dependent on a previous animation, the animation will be
// values. // initialized after the the previous animations have been advanced to the end.
long[] times = ensureChildStartAndEndTimes(); skipToEndValue(false);
long previousTime = -1;
for (long time : times) {
animateBasedOnPlayTime(time, previousTime, false, false);
previousTime = time;
}
} }
} }
@@ -1237,7 +1058,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
for (int i = 0; i < mPlayingSet.size(); i++) { for (int i = 0; i < mPlayingSet.size(); i++) {
Node node = mPlayingSet.get(i); Node node = mPlayingSet.get(i);
if (!node.mEnded) { if (!node.mEnded) {
pulseFrame(node, getPlayTimeForNodeIncludingDelay(unscaledPlayTime, node)); pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
} }
} }
@@ -1308,7 +1129,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
pulseFrame(node, 0); pulseFrame(node, 0);
} else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) { } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
// end event: // end event:
pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); pulseFrame(node, getPlayTimeForNode(playTime, node));
} }
} }
} else { } else {
@@ -1329,7 +1150,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
pulseFrame(node, 0); pulseFrame(node, 0);
} else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) { } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
// start event: // start event:
pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); pulseFrame(node, getPlayTimeForNode(playTime, node));
} }
} }
} }
@@ -1351,15 +1172,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
} }
} }
private long getPlayTimeForNodeIncludingDelay(long overallPlayTime, Node node) { private long getPlayTimeForNode(long overallPlayTime, Node node) {
return getPlayTimeForNodeIncludingDelay(overallPlayTime, node, mReversing); return getPlayTimeForNode(overallPlayTime, node, mReversing);
} }
private long getPlayTimeForNodeIncludingDelay( private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
long overallPlayTime,
Node node,
boolean inReverse
) {
if (inReverse) { if (inReverse) {
overallPlayTime = getTotalDuration() - overallPlayTime; overallPlayTime = getTotalDuration() - overallPlayTime;
return node.mEndTime - overallPlayTime; return node.mEndTime - overallPlayTime;
@@ -1381,8 +1198,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
} }
// Set the child animators to the right end: // Set the child animators to the right end:
if (mShouldResetValuesAtStart) { if (mShouldResetValuesAtStart) {
initChildren(); if (isInitialized()) {
skipToEndValue(!mReversing); skipToEndValue(!mReversing);
} else if (mReversing) {
// Reversing but haven't initialized all the children yet.
initChildren();
skipToEndValue(!mReversing);
} else {
// If not all children are initialized and play direction is forward
for (int i = mEvents.size() - 1; i >= 0; i--) {
if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
Animator anim = mEvents.get(i).mNode.mAnimation;
// Only reset the animations that have been initialized to start value,
// so that if they are defined without a start value, they will get the
// values set at the right time (i.e. the next animation run)
if (anim.isInitialized()) {
anim.skipToEndValue(true);
}
}
}
}
} }
if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
@@ -1457,7 +1292,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
// No longer receive callbacks // No longer receive callbacks
removeAnimationCallback(); removeAnimationCallback();
notifyEndListeners(mReversing); // 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);
}
}
removeAnimationEndListener(); removeAnimationEndListener();
mSelfPulse = true; mSelfPulse = true;
mReversing = false; mReversing = false;
@@ -2079,11 +1922,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
} }
void setPlayTime(long playTime, boolean inReverse) { void setPlayTime(long playTime, boolean inReverse) {
// TODO: This can be simplified.
// Clamp the play time // Clamp the play time
if (getTotalDuration() != DURATION_INFINITE) { if (getTotalDuration() != DURATION_INFINITE) {
mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay); mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
} else {
mPlayTime = playTime;
} }
mPlayTime = Math.max(0, mPlayTime); mPlayTime = Math.max(0, mPlayTime);
mSeekingInReverse = inReverse; mSeekingInReverse = inReverse;

View File

@@ -324,9 +324,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
listenerCopy = new ArrayList<>(sDurationScaleChangeListeners); listenerCopy = new ArrayList<>(sDurationScaleChangeListeners);
} }
int listenersSize = listenerCopy.size(); for (WeakReference<DurationScaleChangeListener> listenerRef : listenerCopy) {
for (int i = 0; i < listenersSize; i++) { final DurationScaleChangeListener listener = listenerRef.get();
final DurationScaleChangeListener listener = listenerCopy.get(i).get();
if (listener != null) { if (listener != null) {
listener.onChanged(durationScale); listener.onChanged(durationScale);
} }
@@ -625,7 +624,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
public void setValues(PropertyValuesHolder... values) { public void setValues(PropertyValuesHolder... values) {
int numValues = values.length; int numValues = values.length;
mValues = values; mValues = values;
mValuesMap = new HashMap<>(numValues); mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) { for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i]; PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
@@ -659,11 +658,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
@CallSuper @CallSuper
void initAnimation() { void initAnimation() {
if (!mInitialized) { if (!mInitialized) {
if (mValues != null) { int numValues = mValues.length;
int numValues = mValues.length; for (int i = 0; i < numValues; ++i) {
for (int i = 0; i < numValues; ++i) { mValues[i].init();
mValues[i].init();
}
} }
mInitialized = true; mInitialized = true;
} }
@@ -1108,30 +1105,18 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
} }
} }
private void notifyStartListeners(boolean isReversing) { private void notifyStartListeners() {
if (mListeners != null && !mStartListenersCalled) { if (mListeners != null && !mStartListenersCalled) {
ArrayList<AnimatorListener> tmpListeners = ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone(); (ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size(); int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) { for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this, isReversing); tmpListeners.get(i).onAnimationStart(this, mReversing);
} }
} }
mStartListenersCalled = true; 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 * 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 * whether the animation should play in reverse. The flag is usually false, but may be set
@@ -1222,16 +1207,12 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
if ((mStarted || mRunning) && mListeners != null) { if ((mStarted || mRunning) && mListeners != null) {
if (!mRunning) { if (!mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now. // If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners(mReversing); notifyStartListeners();
} }
int listenersSize = mListeners.size(); ArrayList<AnimatorListener> tmpListeners =
if (listenersSize > 0) { (ArrayList<AnimatorListener>) mListeners.clone();
ArrayList<AnimatorListener> tmpListeners = for (AnimatorListener listener : tmpListeners) {
(ArrayList<AnimatorListener>) mListeners.clone(); listener.onAnimationCancel(this);
for (int i = 0; i < listenersSize; i++) {
AnimatorListener listener = tmpListeners.get(i);
listener.onAnimationCancel(this);
}
} }
} }
endAnimation(); endAnimation();
@@ -1336,14 +1317,22 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
boolean notify = (mStarted || mRunning) && mListeners != null; boolean notify = (mStarted || mRunning) && mListeners != null;
if (notify && !mRunning) { if (notify && !mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now. // If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners(mReversing); notifyStartListeners();
} }
mRunning = false; mRunning = false;
mStarted = false; mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1; mLastFrameTime = -1;
mFirstFrameTime = -1; mFirstFrameTime = -1;
mStartTime = -1; mStartTime = -1;
notifyEndListeners(mReversing); 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);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks. // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false; mReversing = false;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -1370,8 +1359,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
} else { } else {
mOverallFraction = 0f; mOverallFraction = 0f;
} }
if (mListeners != null) {
notifyStartListeners(mReversing); notifyStartListeners();
}
} }
/** /**
@@ -1462,32 +1452,16 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* will be called. * will be called.
*/ */
@Override @Override
void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) { void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
if (currentPlayTime < 0 || lastPlayTime < -1) { if (currentPlayTime < 0 || lastPlayTime < 0) {
throw new UnsupportedOperationException("Error: Play time should never be negative."); throw new UnsupportedOperationException("Error: Play time should never be negative.");
} }
initAnimation(); 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);
}
lastPlayTime -= mStartDelay;
currentPlayTime -= mStartDelay;
// Check whether repeat callback is needed only when repeat count is non-zero // Check whether repeat callback is needed only when repeat count is non-zero
if (mRepeatCount > 0) { if (mRepeatCount > 0) {
int iteration = Math.max(0, (int) (currentPlayTime / mDuration)); int iteration = (int) (currentPlayTime / mDuration);
int lastIteration = Math.max(0, (int) (lastPlayTime / mDuration)); int lastIteration = (int) (lastPlayTime / mDuration);
// Clamp iteration to [0, mRepeatCount] // Clamp iteration to [0, mRepeatCount]
iteration = Math.min(iteration, mRepeatCount); iteration = Math.min(iteration, mRepeatCount);
@@ -1503,37 +1477,16 @@ 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"); skipToEndValue(inReverse);
} else { } else {
// Find the current fraction: // Find the current fraction:
float fraction = Math.max(0, currentPlayTime) / (float) mDuration; float fraction = currentPlayTime / (float) mDuration;
fraction = getCurrentIterationFraction(fraction, false); fraction = getCurrentIterationFraction(fraction, inReverse);
animateValue(fraction); animateValue(fraction);
} }
} }
@Override
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();
doSkip = duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration;
}
if (doSkip) {
if (notify) {
notifyStartListeners(inReverse);
}
skipToEndValue(inReverse);
if (notify) {
notifyEndListeners(inReverse);
}
}
}
/** /**
* Internal use only. * Internal use only.
* Skips the animation value to end/start, depending on whether the play direction is forward * Skips the animation value to end/start, depending on whether the play direction is forward
@@ -1688,9 +1641,6 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(), Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(),
(int) (fraction * 1000)); (int) (fraction * 1000));
} }
if (mValues == null) {
return;
}
fraction = mInterpolator.getInterpolation(fraction); fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction; mCurrentFraction = fraction;
int numValues = mValues.length; int numValues = mValues.length;