diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java index 2e95c04e31..2a603d763a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -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) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 49d493145c..2579bc2935 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -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)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index 63a79847f4..dc3f79cc53 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -87,12 +87,14 @@ public abstract class TaskViewTouchController 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 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 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; + } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 01e2bf3306..cb83a0d920 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -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 { + private static class LauncherTaskViewController extends TaskViewTouchController { - 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); + } } } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index d196c37593..7f25301ac6 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -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 diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 420a7c4182..77a45bfc03 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -53,7 +53,7 @@ public class WorkspaceStateTransitionAnimation { public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { - setWorkspaceProperty(toState, config.getProperSetter(builder)); + setWorkspaceProperty(toState, config.getPropertySetter(builder)); } public float getFinalScale() { diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index ed9873eaf6..cbc81e4a8c 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -159,7 +159,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil AnimatorSetBuilder builder, AnimationConfig config) { float targetProgress = toState.getVerticalProgress(mLauncher); if (Float.compare(mProgress, targetProgress) == 0) { - setAlphas(toState, config.getProperSetter(builder)); + setAlphas(toState, config.getPropertySetter(builder)); // Fail fast onProgressAnimationEnd(); return; @@ -174,7 +174,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil builder.play(anim); - setAlphas(toState, config.getProperSetter(builder)); + setAlphas(toState, config.getPropertySetter(builder)); } private void setAlphas(LauncherState toState, PropertySetter setter) { diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index 1dba7d665a..8e729e884e 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -35,18 +35,23 @@ import java.util.List; */ public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener { + public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) { + return wrap(anim, duration, null); + } + /** * Creates an animation controller for the provided animation. * The actual duration does not matter as the animation is manually controlled. It just * needs to be larger than the total number of pixels so that we don't have jittering due * to float (animation-fraction * total duration) to int conversion. */ - public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) { + public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration, + Runnable onCancelRunnable) { /** * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed. */ - return new AnimatorPlaybackControllerVL(anim, duration); + return new AnimatorPlaybackControllerVL(anim, duration, onCancelRunnable); } private final ValueAnimator mAnimationPlayer; @@ -58,10 +63,13 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat private Runnable mEndAction; protected boolean mTargetCancelled = false; + protected Runnable mOnCancelRunnable; - protected AnimatorPlaybackController(AnimatorSet anim, long duration) { + protected AnimatorPlaybackController(AnimatorSet anim, long duration, + Runnable onCancelRunnable) { mAnim = anim; mDuration = duration; + mOnCancelRunnable = onCancelRunnable; mAnimationPlayer = ValueAnimator.ofFloat(0, 1); mAnimationPlayer.setInterpolator(Interpolators.LINEAR); @@ -72,6 +80,21 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat @Override public void onAnimationCancel(Animator animation) { mTargetCancelled = true; + if (mOnCancelRunnable != null) { + mOnCancelRunnable.run(); + mOnCancelRunnable = null; + } + } + + @Override + public void onAnimationEnd(Animator animation) { + mTargetCancelled = false; + mOnCancelRunnable = null; + } + + @Override + public void onAnimationStart(Animator animation) { + mTargetCancelled = false; } }); } @@ -163,12 +186,33 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } } + public void dispatchOnCancel() { + dispatchOnCancelRecursively(mAnim); + } + + private void dispatchOnCancelRecursively(Animator animator) { + for (AnimatorListener l : nonNullList(animator.getListeners())) { + l.onAnimationCancel(animator); + } + + if (animator instanceof AnimatorSet) { + for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) { + dispatchOnCancelRecursively(anim); + } + } + } + + public void setOnCancelRunnable(Runnable runnable) { + mOnCancelRunnable = runnable; + } + public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController { private final ValueAnimator[] mChildAnimations; - private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration) { - super(anim, duration); + private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration, + Runnable onCancelRunnable) { + super(anim, duration, onCancelRunnable); // Build animation list ArrayList childAnims = new ArrayList<>(); diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index c0ad110818..d5c0788e14 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -18,10 +18,7 @@ package com.android.launcher3.touch; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.Launcher; @@ -30,13 +27,13 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.util.TouchController; import com.android.launcher3.util.PendingAnimation; +import com.android.launcher3.util.TouchController; /** * TouchController for handling state changes */ -public abstract class AbstractStateChangeTouchController extends AnimatorListenerAdapter +public abstract class AbstractStateChangeTouchController implements TouchController, SwipeDetector.Listener { private static final String TAG = "ASCTouchController"; @@ -146,8 +143,10 @@ public abstract class AbstractStateChangeTouchController extends AnimatorListene mToState = newToState; mStartProgress = 0; + if (mCurrentAnimation != null) { + mCurrentAnimation.setOnCancelRunnable(null); + } mProgressMultiplier = initCurrentAnimation(); - mCurrentAnimation.getTarget().addListener(this); mCurrentAnimation.dispatchOnStart(); return true; } @@ -203,7 +202,6 @@ public abstract class AbstractStateChangeTouchController extends AnimatorListene targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; } - final float endProgress; final float startProgress; final long duration; @@ -220,6 +218,8 @@ public abstract class AbstractStateChangeTouchController extends AnimatorListene endProgress - Math.max(progress, 0)); } } else { + mCurrentAnimation.setOnCancelRunnable(null); + mCurrentAnimation.dispatchOnCancel(); endProgress = 0; if (progress <= 0) { duration = 0; @@ -236,6 +236,7 @@ public abstract class AbstractStateChangeTouchController extends AnimatorListene ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); anim.setFloatValues(startProgress, endProgress); updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling); + mCurrentAnimation.dispatchOnStart(); anim.start(); } @@ -275,13 +276,6 @@ public abstract class AbstractStateChangeTouchController extends AnimatorListene protected void clearState() { mCurrentAnimation = null; mDetector.finishedScrolling(); - } - - @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()); - clearState(); - } + mDetector.setDetectableScrollConditions(0, false); } }