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

Commit 6d452092 authored by Doris Liu's avatar Doris Liu
Browse files

Fix reverse with start delay

Test: cts in the same topic branch

Change-Id: Ie8793f25d39e0104c880f1a873f68650b918bffa
parent 47c4c941
Loading
Loading
Loading
Loading
+133 −48
Original line number Original line Diff line number Diff line
@@ -142,11 +142,17 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
    // the animation was previously seeked and therefore doesn't start from the beginning).
    // the animation was previously seeked and therefore doesn't start from the beginning).
    private final boolean mShouldResetValuesAtStart;
    private final boolean mShouldResetValuesAtStart;


    // In pre-O releases, end() may never explicitly called on a child animator. As a result, end()
    // may not even be properly implemented in a lot of cases. After a few apps crashing on this,
    // it became necessary to use an sdk target guard for calling end().
    private final boolean mEndCanBeCalled;

    // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
    // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
    // not running.
    // not running.
    private long mLastFrameTime = -1;
    private long mLastFrameTime = -1;


    // The time, in milliseconds, when the first frame of the animation came in.
    // The time, in milliseconds, when the first frame of the animation came in. This is the
    // frame before we start counting down the start delay, if any.
    // -1 when the animation is not running.
    // -1 when the animation is not running.
    private long mFirstFrame = -1;
    private long mFirstFrame = -1;


@@ -191,11 +197,12 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        super();
        super();
        mNodeMap.put(mDelayAnim, mRootNode);
        mNodeMap.put(mDelayAnim, mRootNode);
        mNodes.add(mRootNode);
        mNodes.add(mRootNode);
        boolean isPreO;
        // Set the flag to ignore calling end() without start() for pre-N releases
        // Set the flag to ignore calling end() without start() for pre-N releases
        Application app = ActivityThread.currentApplication();
        Application app = ActivityThread.currentApplication();
        if (app == null || app.getApplicationInfo() == null) {
        if (app == null || app.getApplicationInfo() == null) {
            mShouldIgnoreEndWithoutStart = true;
            mShouldIgnoreEndWithoutStart = true;
            mShouldResetValuesAtStart = false;
            isPreO = true;
        } else {
        } else {
            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
                mShouldIgnoreEndWithoutStart = true;
                mShouldIgnoreEndWithoutStart = true;
@@ -203,12 +210,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                mShouldIgnoreEndWithoutStart = false;
                mShouldIgnoreEndWithoutStart = false;
            }
            }


            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O) {
            isPreO = app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O;
                mShouldResetValuesAtStart = false;
            } else {
                mShouldResetValuesAtStart = true;
            }
        }
        }
        mShouldResetValuesAtStart = !isPreO;
        mEndCanBeCalled = !isPreO;
    }
    }


    /**
    /**
@@ -424,6 +429,35 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        }
        }
    }
    }


    // Force all the animations to end when the duration scale is 0.
    private void forceToEnd() {
        if (mEndCanBeCalled) {
            end();
        } else {
            // Note: we don't want to combine this case with the end() method below because in
            // the case of developer calling end(), we still need to make sure end() is explicitly
            // called on the child animators to maintain the old behavior.
            if (mReversing) {
                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
                for (int j = mLastEventId - 1; j >= 0; j--) {
                    AnimationEvent event = mEvents.get(j);
                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
                        event.mNode.mAnimation.reverse();
                    }
                }
            } else {
                for (int j = mLastEventId + 1; j < mEvents.size(); j++) {
                    AnimationEvent event = mEvents.get(j);
                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
                        event.mNode.mAnimation.start();
                    }
                }
            }
            mPlayingSet.clear();
            endAnimation();
        }
    }

    /**
    /**
     * {@inheritDoc}
     * {@inheritDoc}
     *
     *
@@ -445,19 +479,28 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
                for (int j = mLastEventId - 1; j >= 0; j--) {
                for (int j = mLastEventId - 1; j >= 0; j--) {
                    AnimationEvent event = mEvents.get(j);
                    AnimationEvent event = mEvents.get(j);
                    Animator anim = event.mNode.mAnimation;
                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
                        event.mNode.mAnimation.reverse();
                        anim.reverse();
                    } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
                    } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
                        event.mNode.mAnimation.end();
                            && anim.isStarted()) {
                        // Make sure anim hasn't finished before calling end() so that we don't end
                        // already ended animations, which will cause start and end callbacks to be
                        // triggered again.
                        anim.end();
                    }
                    }
                }
                }
            } else {
            } else {
                for (int j = mLastEventId + 1; j < mEvents.size(); j++) {
                for (int j = mLastEventId + 1; j < mEvents.size(); j++) {
                    AnimationEvent event = mEvents.get(j);
                    AnimationEvent event = mEvents.get(j);
                    Animator anim = event.mNode.mAnimation;
                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
                        event.mNode.mAnimation.start();
                        anim.start();
                    } else if (event.mEvent == AnimationEvent.ANIMATION_END) {
                    } else if (event.mEvent == AnimationEvent.ANIMATION_END && anim.isStarted()) {
                        event.mNode.mAnimation.end();
                        // Make sure anim hasn't finished before calling end() so that we don't end
                        // already ended animations, which will cause start and end callbacks to be
                        // triggered again.
                        anim.end();
                    }
                    }
                }
                }
            }
            }
@@ -476,12 +519,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
     */
     */
    @Override
    @Override
    public boolean isRunning() {
    public boolean isRunning() {
        if (mStartDelay > 0) {
        if (mStartDelay == 0) {
            return mStarted && !mDelayAnim.isRunning();
        } else {
            // No start delay, animation should start right away
            return mStarted;
            return mStarted;
        }
        }
        return mLastFrameTime > 0;
    }
    }


    @Override
    @Override
@@ -673,8 +714,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        mReversing = inReverse;
        mReversing = inReverse;


        // Now that all dependencies are set up, start the animations that should be started.
        // Now that all dependencies are set up, start the animations that should be started.
        boolean setIsEmpty = isEmptySet(this);
        boolean isZeroDuration = ValueAnimator.getDurationScale() == 0f || isEmptySet(this);
        if (!setIsEmpty) {
        if (!isZeroDuration) {
            startAnimation();
            startAnimation();
        }
        }


@@ -686,9 +727,10 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
                tmpListeners.get(i).onAnimationStart(this, inReverse);
                tmpListeners.get(i).onAnimationStart(this, inReverse);
            }
            }
        }
        }
        if (setIsEmpty) {
        if (isZeroDuration) {
            // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
            // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
            end();
            // onAnimationEnd() right away.
            forceToEnd();
        }
        }
    }
    }


@@ -815,6 +857,9 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        for (int i = 0; i < unfinishedNodes.size(); i++) {
        for (int i = 0; i < unfinishedNodes.size(); i++) {
            Node node = unfinishedNodes.get(i);
            Node node = unfinishedNodes.get(i);
            long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
            long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
            if (!inReverse) {
                playTime -= node.mAnimation.getStartDelay();
            }
            node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
            node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
        }
        }
    }
    }
@@ -907,8 +952,17 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
     */
     */
    @Override
    @Override
    public boolean doAnimationFrame(long frameTime) {
    public boolean doAnimationFrame(long frameTime) {
        if (mLastFrameTime < 0) {
        float durationScale = ValueAnimator.getDurationScale();
            mFirstFrame = mLastFrameTime = frameTime;
        if (durationScale == 0f) {
            // Duration scale changed to 0 amid animation, end the animation right away.
            forceToEnd();
            return true;
        }

        // After the first frame comes in, we need to wait for start delay to pass before updating
        // any animation values.
        if (mFirstFrame < 0) {
            mFirstFrame = frameTime;
        }
        }


        // Handle pause/resume
        // Handle pause/resume
@@ -928,19 +982,31 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        // Continue at seeked position
        // Continue at seeked position
        if (mSeekState.isActive()) {
        if (mSeekState.isActive()) {
            mSeekState.updateSeekDirection(mReversing);
            mSeekState.updateSeekDirection(mReversing);
            mFirstFrame = frameTime - mSeekState.getPlayTime() - mStartDelay;
            if (mReversing) {
                mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale);
            } else {
                mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay)
                        * durationScale);
            }
            mSeekState.reset();
            mSeekState.reset();
        }
        }


        // This playTime includes the start delay.
        if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) {
        long playTime = frameTime - mFirstFrame;
            // Still during start delay in a forward playing case.
            return false;
        }

        // From here on, we always use unscaled play time. Note this unscaled playtime includes
        // the start delay.
        long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale);
        mLastFrameTime = frameTime;


        // 1. Pulse the animators that will start or end in this frame
        // 1. Pulse the animators that will start or end in this frame
        // 2. Pulse the animators that will finish in a later frame
        // 2. Pulse the animators that will finish in a later frame
        int latestId = findLatestEventIdForTime(playTime);
        int latestId = findLatestEventIdForTime(unscaledPlayTime);
        int startId = mLastEventId;
        int startId = mLastEventId;


        handleAnimationEvents(startId, latestId, playTime);
        handleAnimationEvents(startId, latestId, unscaledPlayTime);


        mLastEventId = latestId;
        mLastEventId = latestId;


@@ -948,8 +1014,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) {
                node.mEnded = node.mAnimation.pulseAnimationFrame(
                pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
                    getPlayTimeForNode(playTime, node));
            }
            }
        }
        }


@@ -960,21 +1025,23 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
            }
            }
        }
        }


        mLastFrameTime = frameTime;
        boolean finished = false;
        if (mPlayingSet.isEmpty()) {
            boolean finished;
        if (mReversing) {
        if (mReversing) {
                // Make sure there's no more END event before current event id and after start delay
            if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) {
                finished = mLastEventId <= 3;
                // The only animation that is running is the delay animation.
                finished = true;
            } else if (mPlayingSet.isEmpty() && mLastEventId < 3) {
                // The only remaining animation is the delay animation
                finished = true;
            }
        } else {
        } else {
                // Make sure there's no more START event before current event id:
            finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1;
                finished = (mLastEventId == mEvents.size() - 1);
        }
        }

        if (finished) {
        if (finished) {
            endAnimation();
            endAnimation();
            return true;
            return true;
        }
        }
        }
        return false;
        return false;
    }
    }


@@ -1029,9 +1096,17 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        }
        }
    }
    }


    private void pulseFrame(Node node, long frameTime) {
    /**
     * This method pulses frames into child animations. It scales the input animation play time
     * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
     *
     * @param node child animator node
     * @param animPlayTime unscaled play time (including start delay) for the child animator
     */
    private void pulseFrame(Node node, long animPlayTime) {
        if (!node.mEnded) {
        if (!node.mEnded) {
            node.mEnded = node.mAnimation.pulseAnimationFrame(frameTime);
            node.mEnded = node.mAnimation.pulseAnimationFrame(
                    (long) (animPlayTime * ValueAnimator.getDurationScale()));
        }
        }
    }
    }


@@ -1052,7 +1127,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
        addDummyListener();
        addDummyListener();


        // Register animation callback
        // Register animation callback
        addAnimationCallback(mStartDelay);
        addAnimationCallback(0);


        if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
        if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
            // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
            // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
@@ -1061,7 +1136,11 @@ 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) {
            if (mReversing || isInitialized()) {
            if (isInitialized()) {
                skipToEndValue(!mReversing);
            } else if (mReversing) {
                // Reversing but haven't initialized all the children yet.
                initChildren();
                skipToEndValue(!mReversing);
                skipToEndValue(!mReversing);
            } else {
            } else {
                // If not all children are initialized and play direction is forward
                // If not all children are initialized and play direction is forward
@@ -1091,6 +1170,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
            }
            }
            int toId = findLatestEventIdForTime(playTime);
            int toId = findLatestEventIdForTime(playTime);
            handleAnimationEvents(-1, toId, playTime);
            handleAnimationEvents(-1, toId, playTime);
            for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
                if (mPlayingSet.get(i).mEnded) {
                    mPlayingSet.remove(i);
                }
            }
            mLastEventId = toId;
            mLastEventId = toId;
        }
        }
    }
    }
@@ -1797,6 +1881,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
            if (mPlayTime >= 0) {
            if (mPlayTime >= 0) {
                if (inReverse != mSeekingInReverse) {
                if (inReverse != mSeekingInReverse) {
                    mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
                    mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
                    mSeekingInReverse = inReverse;
                }
                }
            }
            }
        }
        }
+17 −16
Original line number Original line Diff line number Diff line
@@ -1031,6 +1031,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        // started-but-not-yet-reached-the-first-frame phase.
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = -1;
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        addAnimationCallback(0);
        addAnimationCallback(0);


        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
@@ -1199,7 +1200,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        mStartListenersCalled = false;
        mStartListenersCalled = false;
        mLastFrameTime = -1;
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mFirstFrameTime = -1;
        mReversing = false;
        mStartTime = -1;
        if (notify && mListeners != null) {
        if (notify && mListeners != null) {
            ArrayList<AnimatorListener> tmpListeners =
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
                    (ArrayList<AnimatorListener>) mListeners.clone();
@@ -1208,6 +1209,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
                tmpListeners.get(i).onAnimationEnd(this, mReversing);
                tmpListeners.get(i).onAnimationEnd(this, mReversing);
            }
            }
        }
        }
        // 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)) {
            Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
            Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1392,9 +1394,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
     * @hide
     * @hide
     */
     */
    public final boolean doAnimationFrame(long frameTime) {
    public final boolean doAnimationFrame(long frameTime) {
        if (!mRunning && mStartTime < 0) {
        if (mStartTime < 0) {
            // First frame during delay
            // First frame. If there is start delay, start delay count down will happen *after* this
            mStartTime = frameTime + mStartDelay;
            // frame.
            mStartTime = mReversing ? frameTime : frameTime + mStartDelay;
        }
        }


        // Handle pause/resume
        // Handle pause/resume
@@ -1411,25 +1414,23 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
        }
        }


        if (!mRunning) {
        if (!mRunning) {
            // If not running, that means the animation is in the start delay phase. In the case of
            // If not running, that means the animation is in the start delay phase of a forward
            // reversing, we want to run start delay in the end.
            // running animation. In the case of reversing, we want to run start delay in the end.
            if (mStartTime > frameTime) {
            if (mStartTime > frameTime && mSeekFraction == -1) {
                // During start delay
                // This is when no seek fraction is set during start delay. If developers change the
                // seek fraction during the delay, animation will start from the seeked position
                // right away.
                return false;
                return false;
            } else {
            } else {
                // Start delay has passed.
                // If mRunning is not set by now, that means non-zero start delay,
                // no seeking, not reversing. At this point, start delay has passed.
                mRunning = true;
                mRunning = true;
                startAnimation();
            }
            }
        }
        }


        if (mLastFrameTime < 0) {
        if (mLastFrameTime < 0) {
            // First frame
            if (mSeekFraction >= 0) {
            if (mStartDelay > 0) {
                startAnimation();
            }
            if (mSeekFraction < 0) {
                mStartTime = frameTime;
            } else {
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
                mSeekFraction = -1;