Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05ee5fc9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "cherrypicker-L06600000959260644:N82800001349763665" into udc-dev

* changes:
  Revert^2 "AnimatorSet sends pause/resume for seeked animators"
  Don't notify updates while initializing AnimatorSet
  Call pause() and resume() on child animators.
  Make Animators pass internal tests
parents c9fd9d37 3e832d27
Loading
Loading
Loading
Loading
+126 −17
Original line number Diff line number Diff line
@@ -71,6 +71,20 @@ public abstract class Animator implements Cloneable {
     */
    private static long sBackgroundPauseDelay = 1000;

    /**
     * A cache of the values in a list. Used so that when calling the list, we have a copy
     * of it in case the list is modified while iterating. The array can be reused to avoid
     * allocation on every notification.
     */
    private Object[] mCachedList;

    /**
     * Tracks whether we've notified listeners of the onAnimationStart() event. This can be
     * complex to keep track of since we notify listeners at different times depending on
     * startDelay and whether start() was called before end().
     */
    boolean mStartListenersCalled = false;

    /**
     * Sets the duration for delaying pausing animators when apps go into the background.
     * Used by AnimationHandler when requested to pause animators.
@@ -158,16 +172,11 @@ public abstract class Animator implements Cloneable {
     * @see AnimatorPauseListener
     */
    public void pause() {
        if (isStarted() && !mPaused) {
        // We only want to pause started Animators or animators that setCurrentPlayTime()
        // have been called on. mStartListenerCalled will be true if seek has happened.
        if ((isStarted() || mStartListenersCalled) && !mPaused) {
            mPaused = true;
            if (mPauseListeners != null) {
                ArrayList<AnimatorPauseListener> tmpListeners =
                        (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationPause(this);
                }
            }
            notifyPauseListeners(AnimatorCaller.ON_PAUSE);
        }
    }

@@ -184,14 +193,7 @@ public abstract class Animator implements Cloneable {
    public void resume() {
        if (mPaused) {
            mPaused = false;
            if (mPauseListeners != null) {
                ArrayList<AnimatorPauseListener> tmpListeners =
                        (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationResume(this);
                }
            }
            notifyPauseListeners(AnimatorCaller.ON_RESUME);
        }
    }

@@ -450,6 +452,8 @@ public abstract class Animator implements Cloneable {
            if (mPauseListeners != null) {
                anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
            }
            anim.mCachedList = null;
            anim.mStartListenersCalled = false;
            return anim;
        } catch (CloneNotSupportedException e) {
           throw new AssertionError();
@@ -590,6 +594,86 @@ public abstract class Animator implements Cloneable {
        }
    }

    /**
     * Calls notification for each AnimatorListener.
     *
     * @param notification The notification method to call on each listener.
     * @param isReverse When this is used with start/end, this is the isReverse parameter. For
     *                  other calls, this is ignored.
     */
    void notifyListeners(
            AnimatorCaller<AnimatorListener, Animator> notification,
            boolean isReverse
    ) {
        callOnList(mListeners, notification, this, isReverse);
    }

    /**
     * Call pause/resume on each AnimatorPauseListener.
     *
     * @param notification Either ON_PAUSE or ON_RESUME to call onPause or onResume on each
     *                     listener.
     */
    void notifyPauseListeners(AnimatorCaller<AnimatorPauseListener, Animator> notification) {
        callOnList(mPauseListeners, notification, this, false);
    }

    void notifyStartListeners(boolean isReversing) {
        boolean startListenersCalled = mStartListenersCalled;
        mStartListenersCalled = true;
        if (mListeners != null && !startListenersCalled) {
            notifyListeners(AnimatorCaller.ON_START, isReversing);
        }
    }

    void notifyEndListeners(boolean isReversing) {
        boolean startListenersCalled = mStartListenersCalled;
        mStartListenersCalled = false;
        if (mListeners != null && startListenersCalled) {
            notifyListeners(AnimatorCaller.ON_END, isReversing);
        }
    }

    /**
     * Calls <code>call</code> for every item in <code>list</code> with <code>animator</code> and
     * <code>isReverse</code> as parameters.
     *
     * @param list The list of items to make calls on.
     * @param call The method to call for each item in list.
     * @param animator The animator parameter of call.
     * @param isReverse The isReverse parameter of call.
     * @param <T> The item type of list
     * @param <A> The Animator type of animator.
     */
    <T, A> void callOnList(
            ArrayList<T> list,
            AnimatorCaller<T, A> call,
            A animator,
            boolean isReverse
    ) {
        int size = list == null ? 0 : list.size();
        if (size > 0) {
            // Try to reuse mCacheList to store the items of list.
            Object[] array;
            if (mCachedList == null || mCachedList.length < size) {
                array = new Object[size];
            } else {
                array = mCachedList;
                // Clear it in case there is some reentrancy
                mCachedList = null;
            }
            list.toArray(array);
            for (int i = 0; i < size; i++) {
                //noinspection unchecked
                T item = (T) array[i];
                call.call(item, animator, isReverse);
                array[i] = null;
            }
            // Store it for the next call so we can reuse this array, if needed.
            mCachedList = array;
        }
    }

    /**
     * <p>An animation listener receives notifications from an animation.
     * Notifications indicate animation related events, such as the end or the
@@ -748,4 +832,29 @@ public abstract class Animator implements Cloneable {
            return clone;
        }
    }

    /**
     * Internally used by {@link #callOnList(ArrayList, AnimatorCaller, Object, boolean)} to
     * make a call on all children of a list. This can be for start, stop, pause, cancel, update,
     * etc notifications.
     *
     * @param <T> The type of listener to make the call on
     * @param <A> The type of animator that is passed as a parameter
     */
    interface AnimatorCaller<T, A> {
        void call(T listener, A animator, boolean isReverse);

        AnimatorCaller<AnimatorListener, Animator> ON_START = AnimatorListener::onAnimationStart;
        AnimatorCaller<AnimatorListener, Animator> ON_END = AnimatorListener::onAnimationEnd;
        AnimatorCaller<AnimatorListener, Animator> ON_CANCEL =
                (listener, animator, isReverse) -> listener.onAnimationCancel(animator);
        AnimatorCaller<AnimatorListener, Animator> ON_REPEAT =
                (listener, animator, isReverse) -> listener.onAnimationRepeat(animator);
        AnimatorCaller<AnimatorPauseListener, Animator> ON_PAUSE =
                (listener, animator, isReverse) -> listener.onAnimationPause(animator);
        AnimatorCaller<AnimatorPauseListener, Animator> ON_RESUME =
                (listener, animator, isReverse) -> listener.onAnimationResume(animator);
        AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator> ON_UPDATE =
                (listener, animator, isReverse) -> listener.onAnimationUpdate(animator);
    }
}
+43 −53
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;

/**
 * This class plays a set of {@link Animator} objects in the specified order. Animations
@@ -188,11 +189,6 @@ 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() {
@@ -423,25 +419,29 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        if (isStarted()) {
            ArrayList<AnimatorListener> tmpListeners = null;
            if (mListeners != null) {
                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
                int size = tmpListeners.size();
                for (int i = 0; i < size; i++) {
                    tmpListeners.get(i).onAnimationCancel(this);
                }
            }
            ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
            int setSize = playingSet.size();
            for (int i = 0; i < setSize; i++) {
                playingSet.get(i).mAnimation.cancel();
            }
        if (isStarted() || mStartListenersCalled) {
            notifyListeners(AnimatorCaller.ON_CANCEL, false);
            callOnPlayingSet(Animator::cancel);
            mPlayingSet.clear();
            endAnimation();
        }
    }

    /**
     * Calls consumer on every Animator of mPlayingSet.
     *
     * @param consumer The method to call on every Animator of mPlayingSet.
     */
    private void callOnPlayingSet(Consumer<Animator> consumer) {
        final ArrayList<Node> list = mPlayingSet;
        final int size = list.size();
        //noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < size; i++) {
            final Animator animator = list.get(i).mAnimation;
            consumer.accept(animator);
        }
    }

    // Force all the animations to end when the duration scale is 0.
    private void forceToEnd() {
        if (mEndCanBeCalled) {
@@ -481,13 +481,13 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
            return;
        }
        if (isStarted()) {
            mStarted = false; // don't allow reentrancy
            // Iterate the animations that haven't finished or haven't started, and end them.
            if (mReversing) {
                // Between start() and first frame, mLastEventId would be unset (i.e. -1)
                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
                while (mLastEventId > 0) {
                    mLastEventId = mLastEventId - 1;
                    AnimationEvent event = mEvents.get(mLastEventId);
                for (int eventId = mLastEventId - 1; eventId >= 0; eventId--) {
                    AnimationEvent event = mEvents.get(eventId);
                    Animator anim = event.mNode.mAnimation;
                    if (mNodeMap.get(anim).mEnded) {
                        continue;
@@ -503,11 +503,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                    }
                }
            } else {
                while (mLastEventId < mEvents.size() - 1) {
                for (int eventId = mLastEventId + 1; eventId < mEvents.size(); eventId++) {
                    // Avoid potential reentrant loop caused by child animators manipulating
                    // AnimatorSet's lifecycle (i.e. not a recommended approach).
                    mLastEventId = mLastEventId + 1;
                    AnimationEvent event = mEvents.get(mLastEventId);
                    AnimationEvent event = mEvents.get(eventId);
                    Animator anim = event.mNode.mAnimation;
                    if (mNodeMap.get(anim).mEnded) {
                        continue;
@@ -522,7 +521,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                    }
                }
            }
            mPlayingSet.clear();
        }
        endAnimation();
    }
@@ -662,6 +660,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        super.pause();
        if (!previouslyPaused && mPaused) {
            mPauseTime = -1;
            callOnPlayingSet(Animator::pause);
        }
    }

@@ -676,6 +675,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
            if (mPauseTime >= 0) {
                addAnimationCallback(0);
            }
            callOnPlayingSet(Animator::resume);
        }
    }

@@ -716,6 +716,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        if (inReverse == mReversing && selfPulse == mSelfPulse && mStarted) {
            // It is already started
            return;
        }
        mStarted = true;
        mSelfPulse = selfPulse;
        mPaused = false;
@@ -749,32 +753,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.
    private static boolean isEmptySet(AnimatorSet set) {
        if (set.getStartDelay() > 0) {
@@ -941,12 +919,18 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                                lastPlayTime - node.mStartTime,
                                notify
                        );
                        if (notify) {
                            mPlayingSet.remove(node);
                        }
                    } else if (start <= currentPlayTime && currentPlayTime <= end) {
                        animator.animateSkipToEnds(
                                currentPlayTime - node.mStartTime,
                                lastPlayTime - node.mStartTime,
                                notify
                        );
                        if (notify && !mPlayingSet.contains(node)) {
                            mPlayingSet.add(node);
                        }
                    }
                }
            }
@@ -974,12 +958,18 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                                lastPlayTime - node.mStartTime,
                                notify
                        );
                        if (notify) {
                            mPlayingSet.remove(node);
                        }
                    } else if (start <= currentPlayTime && currentPlayTime <= end) {
                        animator.animateSkipToEnds(
                                currentPlayTime - node.mStartTime,
                                lastPlayTime - node.mStartTime,
                                notify
                        );
                        if (notify && !mPlayingSet.contains(node)) {
                            mPlayingSet.add(node);
                        }
                    }
                }
            }
@@ -1120,8 +1110,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                mSeekState.setPlayTime(0, mReversing);
            }
        }
        animateBasedOnPlayTime(playTime, lastPlayTime, mReversing, true);
        mSeekState.setPlayTime(playTime, mReversing);
        animateBasedOnPlayTime(playTime, lastPlayTime, mReversing, true);
    }

    /**
+13 −63
Original line number Diff line number Diff line
@@ -198,13 +198,6 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
     */
    private boolean mStarted = false;

    /**
     * Tracks whether we've notified listeners of the onAnimationStart() event. This can be
     * complex to keep track of since we notify listeners at different times depending on
     * startDelay and whether start() was called before end().
     */
    private boolean mStartListenersCalled = false;

    /**
     * Flag that denotes whether the animation is set up and ready to go. Used to
     * set up animation that has not yet been started.
@@ -1108,30 +1101,6 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        }
    }

    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, 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
@@ -1149,6 +1118,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        if (playBackwards == mResumed && mSelfPulse == !mSuppressSelfPulseRequested && mStarted) {
            // already started
            return;
        }
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
@@ -1219,23 +1192,14 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        // Only cancel if the animation is actually running or has been started and is about
        // to run
        // Only notify listeners if the animator has actually started
        if ((mStarted || mRunning) && mListeners != null) {
        if ((mStarted || mRunning || mStartListenersCalled) && mListeners != null) {
            if (!mRunning) {
                // If it's not yet running, then start listeners weren't called. Call them now.
                notifyStartListeners(mReversing);
            }
            int listenersSize = mListeners.size();
            if (listenersSize > 0) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
                for (int i = 0; i < listenersSize; i++) {
                    AnimatorListener listener = tmpListeners.get(i);
                    listener.onAnimationCancel(this);
                }
            }
            notifyListeners(AnimatorCaller.ON_CANCEL, false);
        }
        endAnimation();

    }

    @Override
@@ -1338,11 +1302,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
            // If it's not yet running, then start listeners weren't called. Call them now.
            notifyStartListeners(mReversing);
        }
        mRunning = false;
        mStarted = false;
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        mRunning = false;
        mStarted = false;
        notifyEndListeners(mReversing);
        // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
        mReversing = false;
@@ -1435,12 +1399,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
                done = true;
            } else if (newIteration && !lastIterationFinished) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
                notifyListeners(AnimatorCaller.ON_REPEAT, false);
            } else if (lastIterationFinished) {
                done = true;
            }
@@ -1493,13 +1452,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
            iteration = Math.min(iteration, mRepeatCount);
            lastIteration = Math.min(lastIteration, mRepeatCount);

            if (iteration != lastIteration) {
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
            if (notify && iteration != lastIteration) {
                notifyListeners(AnimatorCaller.ON_REPEAT, false);
            }
        }

@@ -1697,11 +1651,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        if (mSeekFraction >= 0 || mStartListenersCalled) {
            callOnList(mUpdateListeners, AnimatorCaller.ON_UPDATE, this, false);
        }
    }

@@ -1718,7 +1669,6 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        anim.mRunning = false;
        anim.mPaused = false;
        anim.mResumed = false;
        anim.mStartListenersCalled = false;
        anim.mStartTime = -1;
        anim.mStartTimeCommitted = false;
        anim.mAnimationEndRequested = false;
+5 −3
Original line number Diff line number Diff line
@@ -435,9 +435,11 @@ public class AnimatorSetActivityTest {
        mActivityRule.runOnUiThread(s::start);

        while (!listener.endIsCalled) {
            boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted() ||
                    a4.isStarted() || a5.isStarted();
            mActivityRule.runOnUiThread(() -> {
                boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted()
                        || a4.isStarted() || a5.isStarted();
                assertEquals(passedStartDelay, s.isRunning());
            });
            Thread.sleep(50);
        }
        assertFalse(s.isRunning());
+526 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading