Loading core/java/android/animation/Animator.java +30 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo.Config; import android.content.res.ConstantState; import android.os.Build; import android.util.LongArray; import java.util.ArrayList; Loading Loading @@ -559,9 +560,36 @@ public abstract class Animator implements Cloneable { } /** * Internal use only. * Internal use only. Changes the value of the animator as if currentPlayTime has passed since * 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) {} /** * 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 animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {} void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {} /** * 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. Loading core/java/android/animation/AnimatorSet.java +195 −90 Original line number Diff line number Diff line Loading @@ -23,9 +23,11 @@ import android.os.Looper; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Log; import android.util.LongArray; import android.view.animation.Animation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; Loading Loading @@ -181,6 +183,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim */ private long mPauseTime = -1; /** * The start and stop times of all descendant animators. */ private long[] mChildStartAndStopTimes; // 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() { Loading Loading @@ -779,26 +786,25 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim @Override 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. initAnimation(); initChildren(); // 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 // the right value in the end. if (inReverse) { for (int i = mEvents.size() - 1; i >= 0; i--) { if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { mEvents.get(i).mNode.mAnimation.skipToEndValue(true); AnimationEvent event = mEvents.get(i); if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { event.mNode.mAnimation.skipToEndValue(true); } } } else { for (int i = 0; i < mEvents.size(); i++) { if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) { mEvents.get(i).mNode.mAnimation.skipToEndValue(false); AnimationEvent event = mEvents.get(i); if (event.mEvent == AnimationEvent.ANIMATION_END) { event.mNode.mAnimation.skipToEndValue(false); } } } Loading @@ -814,72 +820,181 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)}, * as needed, based on the last play time and current play time. */ @Override void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) { if (currentPlayTime < 0 || lastPlayTime < 0) { private void animateBasedOnPlayTime( long currentPlayTime, long lastPlayTime, boolean inReverse ) { if (currentPlayTime < 0 || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); } // 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 (getTotalDuration() == DURATION_INFINITE) { throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite" + " duration"); long duration = getTotalDuration(); if (duration == DURATION_INFINITE) { throw new UnsupportedOperationException( "Cannot reverse AnimatorSet with infinite duration" ); } long duration = getTotalDuration() - mStartDelay; // Convert the play times to the forward direction. currentPlayTime = Math.min(currentPlayTime, duration); currentPlayTime = duration - currentPlayTime; lastPlayTime = duration - lastPlayTime; inReverse = false; } ArrayList<Node> unfinishedNodes = new ArrayList<>(); // Assumes forward playing from here on. for (int i = 0; i < mEvents.size(); i++) { long[] startEndTimes = ensureChildStartAndEndTimes(); int index = findNextIndex(lastPlayTime, startEndTimes); int endIndex = findNextIndex(currentPlayTime, startEndTimes); // 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); animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } index++; } } else { while (index > endIndex) { index--; long playTime = startEndTimes[index]; if (lastPlayTime != playTime) { animateSkipToEnds(playTime, lastPlayTime); animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } } } if (currentPlayTime != lastPlayTime) { animateSkipToEnds(currentPlayTime, lastPlayTime); animateValuesInRange(currentPlayTime, lastPlayTime); } } /** * 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) { initAnimation(); if (lastPlayTime > currentPlayTime) { for (int i = mEvents.size() - 1; i >= 0; i--) { AnimationEvent event = mEvents.get(i); if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) { break; Node node = event.mNode; if (event.mEvent == AnimationEvent.ANIMATION_END && node.mStartTime != DURATION_INFINITE ) { Animator animator = node.mAnimation; long start = node.mStartTime + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (currentPlayTime <= start && start < lastPlayTime) { animator.animateSkipToEnds( start - node.mStartTime, lastPlayTime - node.mStartTime ); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, lastPlayTime - node.mStartTime ); } } } } else { 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 + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (lastPlayTime < end && end <= currentPlayTime) { animator.animateSkipToEnds( end - node.mStartTime, lastPlayTime - node.mStartTime ); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, lastPlayTime - node.mStartTime ); } } } } } // This animation started prior to the current play time, and won't finish before the // play time, add to the unfinished list. if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { if (event.mNode.mEndTime == DURATION_INFINITE || event.mNode.mEndTime > currentPlayTime) { unfinishedNodes.add(event.mNode); @Override void animateValuesInRange(long currentPlayTime, long lastPlayTime) { initAnimation(); 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 + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (start < currentPlayTime && currentPlayTime < end) { animator.animateValuesInRange( currentPlayTime - node.mStartTime, Math.max(-1, lastPlayTime - node.mStartTime) ); } } // 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) { // Skip to the end of the animation. event.mNode.mAnimation.skipToEndValue(false); } } // 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(); private long[] ensureChildStartAndEndTimes() { if (mChildStartAndStopTimes == null) { LongArray startAndEndTimes = new LongArray(); getStartAndEndTimes(startAndEndTimes, 0); long[] times = startAndEndTimes.toArray(); Arrays.sort(times); mChildStartAndStopTimes = times; } node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse); return mChildStartAndStopTimes; } // Seek not yet started animations. for (int i = 0; i < mEvents.size(); i++) { @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.getTime() > currentPlayTime && event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { event.mNode.mAnimation.skipToEndValue(true); if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && event.mNode.mStartTime != DURATION_INFINITE ) { event.mNode.mAnimation.getStartAndEndTimes(times, offset + event.mNode.mStartTime); } } } @Override Loading @@ -899,10 +1014,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim return mChildrenInitialized; } private void skipToStartValue(boolean inReverse) { skipToEndValue(!inReverse); } /** * 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 Loading Loading @@ -932,17 +1043,19 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim initAnimation(); if (!isStarted() || isPaused()) { if (mReversing) { 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); // Set all the values to start values. initChildren(); // Set all the values to start values. skipToEndValue(!mReversing); mSeekState.setPlayTime(0, mReversing); } animateBasedOnPlayTime(playTime, 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 Loading Loading @@ -981,10 +1094,16 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void initChildren() { if (!isInitialized()) { mChildrenInitialized = true; // Forcefully initialize all children based on their end time, so that if the start // value of a child is dependent on a previous animation, the animation will be // initialized after the the previous animations have been advanced to the end. skipToEndValue(false); // We have to initialize all the start values so that they are based on the previous // values. long[] times = ensureChildStartAndEndTimes(); long previousTime = -1; for (long time : times) { animateBasedOnPlayTime(time, previousTime, false); previousTime = time; } } } Loading Loading @@ -1058,7 +1177,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim for (int i = 0; i < mPlayingSet.size(); i++) { Node node = mPlayingSet.get(i); if (!node.mEnded) { pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(unscaledPlayTime, node)); } } Loading Loading @@ -1129,7 +1248,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim pulseFrame(node, 0); } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) { // end event: pulseFrame(node, getPlayTimeForNode(playTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); } } } else { Loading @@ -1150,7 +1269,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim pulseFrame(node, 0); } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) { // start event: pulseFrame(node, getPlayTimeForNode(playTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); } } } Loading @@ -1172,11 +1291,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } } private long getPlayTimeForNode(long overallPlayTime, Node node) { return getPlayTimeForNode(overallPlayTime, node, mReversing); private long getPlayTimeForNodeIncludingDelay(long overallPlayTime, Node node) { return getPlayTimeForNodeIncludingDelay(overallPlayTime, node, mReversing); } private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) { private long getPlayTimeForNodeIncludingDelay( long overallPlayTime, Node node, boolean inReverse ) { if (inReverse) { overallPlayTime = getTotalDuration() - overallPlayTime; return node.mEndTime - overallPlayTime; Loading @@ -1198,26 +1321,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { if (isInitialized()) { 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()) { Loading Loading @@ -1922,11 +2027,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } void setPlayTime(long playTime, boolean inReverse) { // TODO: This can be simplified. // Clamp the play time if (getTotalDuration() != DURATION_INFINITE) { mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay); } else { mPlayTime = playTime; } mPlayTime = Math.max(0, mPlayTime); mSeekingInReverse = inReverse; Loading core/java/android/animation/ValueAnimator.java +43 −14 Original line number Diff line number Diff line Loading @@ -324,8 +324,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio listenerCopy = new ArrayList<>(sDurationScaleChangeListeners); } for (WeakReference<DurationScaleChangeListener> listenerRef : listenerCopy) { final DurationScaleChangeListener listener = listenerRef.get(); int listenersSize = listenerCopy.size(); for (int i = 0; i < listenersSize; i++) { final DurationScaleChangeListener listener = listenerCopy.get(i).get(); if (listener != null) { listener.onChanged(durationScale); } Loading Loading @@ -624,7 +625,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio public void setValues(PropertyValuesHolder... values) { int numValues = values.length; mValues = values; mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); mValuesMap = new HashMap<>(numValues); for (int i = 0; i < numValues; ++i) { PropertyValuesHolder valuesHolder = values[i]; mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); Loading Loading @@ -658,10 +659,12 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio @CallSuper void initAnimation() { if (!mInitialized) { if (mValues != null) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } } mInitialized = true; } } Loading Loading @@ -1209,12 +1212,16 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio // If it's not yet running, then start listeners weren't called. Call them now. notifyStartListeners(); } int listenersSize = mListeners.size(); if (listenersSize > 0) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); for (AnimatorListener listener : tmpListeners) { for (int i = 0; i < listenersSize; i++) { AnimatorListener listener = tmpListeners.get(i); listener.onAnimationCancel(this); } } } endAnimation(); } Loading Loading @@ -1452,12 +1459,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * will be called. */ @Override void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) { if (currentPlayTime < 0 || lastPlayTime < 0) { void animateValuesInRange(long currentPlayTime, long lastPlayTime) { if (currentPlayTime < mStartDelay || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); } initAnimation(); long duration = getTotalDuration(); 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 if (mRepeatCount > 0) { int iteration = (int) (currentPlayTime / mDuration); Loading @@ -1478,15 +1492,27 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio } if (mRepeatCount != INFINITE && currentPlayTime >= (mRepeatCount + 1) * mDuration) { skipToEndValue(inReverse); throw new IllegalStateException("Can't animate a value outside of the duration"); } else { // Find the current fraction: float fraction = currentPlayTime / (float) mDuration; fraction = getCurrentIterationFraction(fraction, inReverse); fraction = getCurrentIterationFraction(fraction, false); animateValue(fraction); } } @Override void animateSkipToEnds(long currentPlayTime, long lastPlayTime) { if (currentPlayTime <= mStartDelay && lastPlayTime > mStartDelay) { skipToEndValue(true); } else { long duration = getTotalDuration(); if (duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration) { skipToEndValue(false); } } } /** * Internal use only. * Skips the animation value to end/start, depending on whether the play direction is forward Loading Loading @@ -1641,6 +1667,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(), (int) (fraction * 1000)); } if (mValues == null) { return; } fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; Loading Loading
core/java/android/animation/Animator.java +30 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo.Config; import android.content.res.ConstantState; import android.os.Build; import android.util.LongArray; import java.util.ArrayList; Loading Loading @@ -559,9 +560,36 @@ public abstract class Animator implements Cloneable { } /** * Internal use only. * Internal use only. Changes the value of the animator as if currentPlayTime has passed since * 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) {} /** * 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 animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {} void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {} /** * 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. Loading
core/java/android/animation/AnimatorSet.java +195 −90 Original line number Diff line number Diff line Loading @@ -23,9 +23,11 @@ import android.os.Looper; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.Log; import android.util.LongArray; import android.view.animation.Animation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; Loading Loading @@ -181,6 +183,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim */ private long mPauseTime = -1; /** * The start and stop times of all descendant animators. */ private long[] mChildStartAndStopTimes; // 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() { Loading Loading @@ -779,26 +786,25 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim @Override 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. initAnimation(); initChildren(); // 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 // the right value in the end. if (inReverse) { for (int i = mEvents.size() - 1; i >= 0; i--) { if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { mEvents.get(i).mNode.mAnimation.skipToEndValue(true); AnimationEvent event = mEvents.get(i); if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { event.mNode.mAnimation.skipToEndValue(true); } } } else { for (int i = 0; i < mEvents.size(); i++) { if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) { mEvents.get(i).mNode.mAnimation.skipToEndValue(false); AnimationEvent event = mEvents.get(i); if (event.mEvent == AnimationEvent.ANIMATION_END) { event.mNode.mAnimation.skipToEndValue(false); } } } Loading @@ -814,72 +820,181 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)}, * as needed, based on the last play time and current play time. */ @Override void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) { if (currentPlayTime < 0 || lastPlayTime < 0) { private void animateBasedOnPlayTime( long currentPlayTime, long lastPlayTime, boolean inReverse ) { if (currentPlayTime < 0 || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); } // 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 (getTotalDuration() == DURATION_INFINITE) { throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite" + " duration"); long duration = getTotalDuration(); if (duration == DURATION_INFINITE) { throw new UnsupportedOperationException( "Cannot reverse AnimatorSet with infinite duration" ); } long duration = getTotalDuration() - mStartDelay; // Convert the play times to the forward direction. currentPlayTime = Math.min(currentPlayTime, duration); currentPlayTime = duration - currentPlayTime; lastPlayTime = duration - lastPlayTime; inReverse = false; } ArrayList<Node> unfinishedNodes = new ArrayList<>(); // Assumes forward playing from here on. for (int i = 0; i < mEvents.size(); i++) { long[] startEndTimes = ensureChildStartAndEndTimes(); int index = findNextIndex(lastPlayTime, startEndTimes); int endIndex = findNextIndex(currentPlayTime, startEndTimes); // 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); animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } index++; } } else { while (index > endIndex) { index--; long playTime = startEndTimes[index]; if (lastPlayTime != playTime) { animateSkipToEnds(playTime, lastPlayTime); animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } } } if (currentPlayTime != lastPlayTime) { animateSkipToEnds(currentPlayTime, lastPlayTime); animateValuesInRange(currentPlayTime, lastPlayTime); } } /** * 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) { initAnimation(); if (lastPlayTime > currentPlayTime) { for (int i = mEvents.size() - 1; i >= 0; i--) { AnimationEvent event = mEvents.get(i); if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) { break; Node node = event.mNode; if (event.mEvent == AnimationEvent.ANIMATION_END && node.mStartTime != DURATION_INFINITE ) { Animator animator = node.mAnimation; long start = node.mStartTime + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (currentPlayTime <= start && start < lastPlayTime) { animator.animateSkipToEnds( start - node.mStartTime, lastPlayTime - node.mStartTime ); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, lastPlayTime - node.mStartTime ); } } } } else { 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 + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (lastPlayTime < end && end <= currentPlayTime) { animator.animateSkipToEnds( end - node.mStartTime, lastPlayTime - node.mStartTime ); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, lastPlayTime - node.mStartTime ); } } } } } // This animation started prior to the current play time, and won't finish before the // play time, add to the unfinished list. if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { if (event.mNode.mEndTime == DURATION_INFINITE || event.mNode.mEndTime > currentPlayTime) { unfinishedNodes.add(event.mNode); @Override void animateValuesInRange(long currentPlayTime, long lastPlayTime) { initAnimation(); 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 + animator.getStartDelay(); long end = node.mTotalDuration == DURATION_INFINITE ? Long.MAX_VALUE : node.mEndTime; if (start < currentPlayTime && currentPlayTime < end) { animator.animateValuesInRange( currentPlayTime - node.mStartTime, Math.max(-1, lastPlayTime - node.mStartTime) ); } } // 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) { // Skip to the end of the animation. event.mNode.mAnimation.skipToEndValue(false); } } // 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(); private long[] ensureChildStartAndEndTimes() { if (mChildStartAndStopTimes == null) { LongArray startAndEndTimes = new LongArray(); getStartAndEndTimes(startAndEndTimes, 0); long[] times = startAndEndTimes.toArray(); Arrays.sort(times); mChildStartAndStopTimes = times; } node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse); return mChildStartAndStopTimes; } // Seek not yet started animations. for (int i = 0; i < mEvents.size(); i++) { @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.getTime() > currentPlayTime && event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { event.mNode.mAnimation.skipToEndValue(true); if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && event.mNode.mStartTime != DURATION_INFINITE ) { event.mNode.mAnimation.getStartAndEndTimes(times, offset + event.mNode.mStartTime); } } } @Override Loading @@ -899,10 +1014,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim return mChildrenInitialized; } private void skipToStartValue(boolean inReverse) { skipToEndValue(!inReverse); } /** * 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 Loading Loading @@ -932,17 +1043,19 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim initAnimation(); if (!isStarted() || isPaused()) { if (mReversing) { 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); // Set all the values to start values. initChildren(); // Set all the values to start values. skipToEndValue(!mReversing); mSeekState.setPlayTime(0, mReversing); } animateBasedOnPlayTime(playTime, 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 Loading Loading @@ -981,10 +1094,16 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void initChildren() { if (!isInitialized()) { mChildrenInitialized = true; // Forcefully initialize all children based on their end time, so that if the start // value of a child is dependent on a previous animation, the animation will be // initialized after the the previous animations have been advanced to the end. skipToEndValue(false); // We have to initialize all the start values so that they are based on the previous // values. long[] times = ensureChildStartAndEndTimes(); long previousTime = -1; for (long time : times) { animateBasedOnPlayTime(time, previousTime, false); previousTime = time; } } } Loading Loading @@ -1058,7 +1177,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim for (int i = 0; i < mPlayingSet.size(); i++) { Node node = mPlayingSet.get(i); if (!node.mEnded) { pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(unscaledPlayTime, node)); } } Loading Loading @@ -1129,7 +1248,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim pulseFrame(node, 0); } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) { // end event: pulseFrame(node, getPlayTimeForNode(playTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); } } } else { Loading @@ -1150,7 +1269,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim pulseFrame(node, 0); } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) { // start event: pulseFrame(node, getPlayTimeForNode(playTime, node)); pulseFrame(node, getPlayTimeForNodeIncludingDelay(playTime, node)); } } } Loading @@ -1172,11 +1291,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } } private long getPlayTimeForNode(long overallPlayTime, Node node) { return getPlayTimeForNode(overallPlayTime, node, mReversing); private long getPlayTimeForNodeIncludingDelay(long overallPlayTime, Node node) { return getPlayTimeForNodeIncludingDelay(overallPlayTime, node, mReversing); } private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) { private long getPlayTimeForNodeIncludingDelay( long overallPlayTime, Node node, boolean inReverse ) { if (inReverse) { overallPlayTime = getTotalDuration() - overallPlayTime; return node.mEndTime - overallPlayTime; Loading @@ -1198,26 +1321,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { if (isInitialized()) { 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()) { Loading Loading @@ -1922,11 +2027,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } void setPlayTime(long playTime, boolean inReverse) { // TODO: This can be simplified. // Clamp the play time if (getTotalDuration() != DURATION_INFINITE) { mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay); } else { mPlayTime = playTime; } mPlayTime = Math.max(0, mPlayTime); mSeekingInReverse = inReverse; Loading
core/java/android/animation/ValueAnimator.java +43 −14 Original line number Diff line number Diff line Loading @@ -324,8 +324,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio listenerCopy = new ArrayList<>(sDurationScaleChangeListeners); } for (WeakReference<DurationScaleChangeListener> listenerRef : listenerCopy) { final DurationScaleChangeListener listener = listenerRef.get(); int listenersSize = listenerCopy.size(); for (int i = 0; i < listenersSize; i++) { final DurationScaleChangeListener listener = listenerCopy.get(i).get(); if (listener != null) { listener.onChanged(durationScale); } Loading Loading @@ -624,7 +625,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio public void setValues(PropertyValuesHolder... values) { int numValues = values.length; mValues = values; mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); mValuesMap = new HashMap<>(numValues); for (int i = 0; i < numValues; ++i) { PropertyValuesHolder valuesHolder = values[i]; mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); Loading Loading @@ -658,10 +659,12 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio @CallSuper void initAnimation() { if (!mInitialized) { if (mValues != null) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } } mInitialized = true; } } Loading Loading @@ -1209,12 +1212,16 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio // If it's not yet running, then start listeners weren't called. Call them now. notifyStartListeners(); } int listenersSize = mListeners.size(); if (listenersSize > 0) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); for (AnimatorListener listener : tmpListeners) { for (int i = 0; i < listenersSize; i++) { AnimatorListener listener = tmpListeners.get(i); listener.onAnimationCancel(this); } } } endAnimation(); } Loading Loading @@ -1452,12 +1459,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * will be called. */ @Override void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) { if (currentPlayTime < 0 || lastPlayTime < 0) { void animateValuesInRange(long currentPlayTime, long lastPlayTime) { if (currentPlayTime < mStartDelay || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); } initAnimation(); long duration = getTotalDuration(); 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 if (mRepeatCount > 0) { int iteration = (int) (currentPlayTime / mDuration); Loading @@ -1478,15 +1492,27 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio } if (mRepeatCount != INFINITE && currentPlayTime >= (mRepeatCount + 1) * mDuration) { skipToEndValue(inReverse); throw new IllegalStateException("Can't animate a value outside of the duration"); } else { // Find the current fraction: float fraction = currentPlayTime / (float) mDuration; fraction = getCurrentIterationFraction(fraction, inReverse); fraction = getCurrentIterationFraction(fraction, false); animateValue(fraction); } } @Override void animateSkipToEnds(long currentPlayTime, long lastPlayTime) { if (currentPlayTime <= mStartDelay && lastPlayTime > mStartDelay) { skipToEndValue(true); } else { long duration = getTotalDuration(); if (duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration) { skipToEndValue(false); } } } /** * Internal use only. * Skips the animation value to end/start, depending on whether the play direction is forward Loading Loading @@ -1641,6 +1667,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(), (int) (fraction * 1000)); } if (mValues == null) { return; } fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; Loading