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

Commit 31fbd4c0 authored by Tony's avatar Tony
Browse files

Fix some state issues with user-controlled animations

Previously, user-controlled animations weren't properly being canceled when a
non-user-controlled animation started, e.g. when hitting home. Thus, we could
end in the wrong or inconsistent state because the user-controlled animation's
end runnable was still used. Now we add a cleanup callback for when we reset
the user-controlled animation for one that isn't user-controlled.

Also fixed a couple typos.

Tests (easier with animation durations extended):
- Swipe up and hit home before reaching overview -> land on home
- Go to overview, swipe down slightly (before threshold to go to workspace)
  and let go -> return to overview without flash (recents was resetting)
- Swipe up, press home while swiping -> goes home, stops responding to drag
- Start dismissing task and hit home before it finishes (or while dragging)
  -> goes home, stops responding to drag

Bug: 78249220
Change-Id: If11d8999e3fadba38c987b25af67cd2304cd859b
parent 3aa3eb51
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -187,10 +187,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
            builder = new AnimatorSetBuilder();
        }

        if (mPendingAnimation != null) {
            mPendingAnimation.finish(false, Touch.SWIPE);
            mPendingAnimation = null;
        }
        cancelPendingAnim();

        RecentsView recentsView = mLauncher.getOverviewPanel();
        TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
@@ -199,10 +196,16 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
            mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);

            mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy);
            Runnable onCancelRunnable = () -> {
                cancelPendingAnim();
                clearState();
            };
            mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
                    onCancelRunnable);
            mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
        } else {
            mCurrentAnimation = mLauncher.getStateManager()
                    .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
                    .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState);
        }

        if (totalShift == 0) {
@@ -212,6 +215,13 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
        return 1 / totalShift;
    }

    private void cancelPendingAnim() {
        if (mPendingAnimation != null) {
            mPendingAnimation.finish(false, Touch.SWIPE);
            mPendingAnimation = null;
        }
    }

    @Override
    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
            LauncherState targetState, float velocity, boolean isFling) {
+1 −1
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ public class RecentsViewStateController implements StateHandler {
    @Override
    public void setStateWithAnimation(final LauncherState toState,
            AnimatorSetBuilder builder, AnimationConfig config) {
        PropertySetter setter = config.getProperSetter(builder);
        PropertySetter setter = config.getPropertySetter(builder);
        float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
        setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0],
                builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
+18 −3
Original line number Diff line number Diff line
@@ -87,12 +87,14 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>

    protected abstract boolean isRecentsInteractive();

    protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
            Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
            mDetector.finishedScrolling();
            mCurrentAnimation = null;
            clearState();
        }
    }

@@ -194,8 +196,12 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
            mEndDisplacement = dl.getHeight() - mTempCords[1];
        }

        if (mCurrentAnimation != null) {
            mCurrentAnimation.setOnCancelRunnable(null);
        }
        mCurrentAnimation = AnimatorPlaybackController
                .wrap(mPendingAnimation.anim, maxDuration);
                .wrap(mPendingAnimation.anim, maxDuration, this::clearState);
        onUserControlledAnimationCreated(mCurrentAnimation);
        mCurrentAnimation.getTarget().addListener(this);
        mCurrentAnimation.dispatchOnStart();
        mProgressMultiplier = 1 / mEndDisplacement;
@@ -271,8 +277,17 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
            mPendingAnimation.finish(wasSuccess, logAction);
            mPendingAnimation = null;
        }
        clearState();
    }

    private void clearState() {
        mDetector.finishedScrolling();
        mDetector.setDetectableScrollConditions(0, false);
        mTaskBeingDragged = null;
        mCurrentAnimation = null;
        if (mPendingAnimation != null) {
            mPendingAnimation.finish(false, Touch.SWIPE);
            mPendingAnimation = null;
        }
    }
}
+11 −5
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
@@ -44,19 +45,19 @@ public class UiFactory {
            return new TouchController[] {
                    launcher.getDragController(),
                    new OverviewToAllAppsTouchController(launcher),
                    new LauncherTaskViewcontroller(launcher)};
                    new LauncherTaskViewController(launcher)};
        }
        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
            return new TouchController[] {
                    launcher.getDragController(),
                    new OverviewToAllAppsTouchController(launcher),
                    new LandscapeEdgeSwipeController(launcher),
                    new LauncherTaskViewcontroller(launcher)};
                    new LauncherTaskViewController(launcher)};
        } else {
            return new TouchController[] {
                    launcher.getDragController(),
                    new PortraitStatesTouchController(launcher),
                    new LauncherTaskViewcontroller(launcher)};
                    new LauncherTaskViewController(launcher)};
        }
    }

@@ -114,9 +115,9 @@ public class UiFactory {
        }
    }

    private static class LauncherTaskViewcontroller extends TaskViewTouchController<Launcher> {
    private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {

        public LauncherTaskViewcontroller(Launcher activity) {
        public LauncherTaskViewController(Launcher activity) {
            super(activity);
        }

@@ -124,5 +125,10 @@ public class UiFactory {
        protected boolean isRecentsInteractive() {
            return mActivity.isInState(OVERVIEW);
        }

        @Override
        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
        }
    }
}
+30 −13
Original line number Diff line number Diff line
@@ -238,16 +238,18 @@ public class LauncherStateManager {
     */
    public AnimatorPlaybackController createAnimationToNewWorkspace(
            LauncherState state, long duration) {
        return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration);
        return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null);
    }

    public AnimatorPlaybackController createAnimationToNewWorkspace(
            LauncherState state, AnimatorSetBuilder builder, long duration) {
    public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
            AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable) {
        mConfig.reset();
        mConfig.userControlled = true;
        mConfig.duration = duration;
        return AnimatorPlaybackController.wrap(
                createAnimationToNewWorkspaceInternal(state, builder, null), duration);
        mConfig.playbackController = AnimatorPlaybackController.wrap(
                createAnimationToNewWorkspaceInternal(state, builder, null), duration,
                onCancelRunnable);
        return mConfig.playbackController;
    }

    protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
@@ -358,6 +360,12 @@ public class LauncherStateManager {
        mConfig.reset();
    }

    public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
        setCurrentAnimation(controller.getTarget());
        mConfig.userControlled = true;
        mConfig.playbackController = controller;
    }

    /**
     * Sets the animation as the current state animation, i.e., canceled when
     * starting another animation and may block some launcher interactions while running.
@@ -405,30 +413,39 @@ public class LauncherStateManager {
    public static class AnimationConfig extends AnimatorListenerAdapter {
        public long duration;
        public boolean userControlled;
        private PropertySetter mProperSetter;
        public AnimatorPlaybackController playbackController;
        private PropertySetter mPropertySetter;

        private AnimatorSet mCurrentAnimation;
        private LauncherState mTargetState;

        /**
         * Cancels the current animation and resets config variables.
         */
        public void reset() {
            duration = 0;
            userControlled = false;
            mProperSetter = null;
            mPropertySetter = null;
            mTargetState = null;

            if (mCurrentAnimation != null) {
            if (playbackController != null) {
                playbackController.getAnimationPlayer().cancel();
                playbackController.dispatchOnCancel();
            } else if (mCurrentAnimation != null) {
                mCurrentAnimation.setDuration(0);
                mCurrentAnimation.cancel();
                mCurrentAnimation = null;
            }

            mCurrentAnimation = null;
            playbackController = null;
        }

        public PropertySetter getProperSetter(AnimatorSetBuilder builder) {
            if (mProperSetter == null) {
                mProperSetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
        public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
            if (mPropertySetter == null) {
                mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
                        : new AnimatedPropertySetter(duration, builder);
            }
            return mProperSetter;
            return mPropertySetter;
        }

        @Override
Loading