diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index 5181a86960..f73e2f2f0a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -17,6 +17,8 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU; +import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; +import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; @@ -24,7 +26,6 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; -import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.animation.ValueAnimator; @@ -186,8 +187,8 @@ public class NavBarToHomeTouchController implements TouchController, if (topView != null) { topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder); } - mCurrentAnimation = builder.createPlaybackController() - .setOnCancelRunnable(this::clearState); + mCurrentAnimation = builder.createPlaybackController(); + mCurrentAnimation.getTarget().addListener(newCancelListener(this::clearState)); } private void clearState() { diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 2a3bdbfcb8..702c519ace 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; @@ -191,15 +192,17 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch return; } mNormalToHintOverviewScrimAnimator = null; - mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> { + mCurrentAnimation.getTarget().addListener(newCancelListener(() -> mLauncher.getStateManager().goToState(OVERVIEW, true, () -> { mOverviewResistYAnim = AnimatorControllerWithResistance .createRecentsResistanceFromOverviewAnim(mLauncher, null) .createPlaybackController(); mReachedOverview = true; maybeSwipeInteractionToOverviewComplete(); - }); - }); + }))); + + mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener); + mCurrentAnimation.dispatchOnCancel(); mStartedOverview = true; VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index f378848a71..df433f8cc0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; @@ -44,6 +45,7 @@ import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.graphics.PointF; @@ -92,6 +94,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; private final LauncherRecentsView mRecentsView; + protected final AnimatorListener mClearStateOnCancelListener = + newCancelListener(this::clearState); private boolean mNoIntercept; private LauncherState mStartState; @@ -204,8 +208,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, config.duration = (long) (Math.max(mXRange, mYRange) * 2); config.animFlags = config.animFlags | SKIP_OVERVIEW; mNonOverviewAnim = mLauncher.getStateManager() - .createAnimationToNewWorkspace(toState, config) - .setOnCancelRunnable(this::clearState); + .createAnimationToNewWorkspace(toState, config); + mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener); } private void setupOverviewAnimators() { @@ -379,7 +383,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, if (canceled) { // Let the state manager know that the animation didn't go to the target state, // but don't clean up yet (we already clean up when the animation completes). - mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable(); + mNonOverviewAnim.getTarget().removeListener(mClearStateOnCancelListener); + mNonOverviewAnim.dispatchOnCancel(); } float startProgress = mNonOverviewAnim.getProgressFraction(); float endProgress = canceled ? 0 : 1; diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 037d9889fd..3c9b808f2f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -245,29 +245,29 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr config.animFlags = animFlags; config.duration = maxAccuracy; - cancelPendingAnim(); + if (mCurrentAnimation != null) { + mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener); + mCurrentAnimation.dispatchOnCancel(); + } + mGoingBetweenStates = true; if (mFromState == OVERVIEW && mToState == NORMAL && mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) { // Reset the state manager, when changing the interaction mode mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */); - mPendingAnimation = mOverviewPortraitStateTouchHelper - .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR); - Runnable onCancelRunnable = () -> { - cancelPendingAnim(); - clearState(); - }; - mCurrentAnimation = mPendingAnimation.createPlaybackController() - .setOnCancelRunnable(onCancelRunnable); + mGoingBetweenStates = false; + mCurrentAnimation = mOverviewPortraitStateTouchHelper + .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR) + .createPlaybackController(); mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation); RecentsView recentsView = mLauncher.getOverviewPanel(); totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler()); } else { mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, config) - .setOnCancelRunnable(this::clearState); + .createAnimationToNewWorkspace(mToState, config); } + mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener); if (totalShift == 0) { totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) @@ -276,13 +276,6 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return 1 / totalShift; } - private void cancelPendingAnim() { - if (mPendingAnimation != null) { - mPendingAnimation.finish(false); - 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/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index d39b6f5d97..fc9e1bbfe5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -106,8 +106,8 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll setupInterpolators(config); config.duration = (long) (getShiftRange() * 2); mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, config) - .setOnCancelRunnable(this::clearState); + .createAnimationToNewWorkspace(mToState, config); + mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener); mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> updateFullscreenProgress((Float) valueAnimator.getAnimatedValue())); return 1 / getShiftRange(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 186caf6dc1..97296957fe 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; +import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE; @@ -50,16 +51,12 @@ public abstract class TaskViewTouchController extends AnimatorListenerAdapter implements TouchController, SingleAxisSwipeDetector.Listener { - // Progress after which the transition is assumed to be a success in case user does not fling - public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; - protected final T mActivity; private final SingleAxisSwipeDetector mDetector; private final RecentsView mRecentsView; private final int[] mTempCords = new int[2]; private final boolean mIsRtl; - private PendingAnimation mPendingAnimation; private AnimatorPlaybackController mCurrentAnimation; private boolean mCurrentAnimationIsGoingUp; @@ -200,10 +197,8 @@ public abstract class TaskViewTouchController } if (mCurrentAnimation != null) { mCurrentAnimation.setPlayFraction(0); - } - if (mPendingAnimation != null) { - mPendingAnimation.finish(false); - mPendingAnimation = null; + mCurrentAnimation.getTarget().removeListener(this); + mCurrentAnimation.dispatchOnCancel(); } PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); @@ -216,15 +211,16 @@ public abstract class TaskViewTouchController // The interpolator controlling the most prominent visual movement. We use this to determine // whether we passed SUCCESS_TRANSITION_PROGRESS. final Interpolator currentInterpolator; + PendingAnimation pa; if (goingUp) { currentInterpolator = Interpolators.LINEAR; - mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, + pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, true /* animateTaskView */, true /* removeTask */, maxDuration); mEndDisplacement = -secondaryTaskDimension; } else { currentInterpolator = Interpolators.ZOOM_IN; - mPendingAnimation = mRecentsView.createTaskLaunchAnimation( + pa = mRecentsView.createTaskLaunchAnimation( mTaskBeingDragged, maxDuration, currentInterpolator); // Since the thumbnail is what is filling the screen, based the end displacement on it. @@ -234,12 +230,8 @@ public abstract class TaskViewTouchController mEndDisplacement = secondaryLayerDimension - mTempCords[1]; } mEndDisplacement *= verticalFactor; + mCurrentAnimation = pa.createPlaybackController(); - if (mCurrentAnimation != null) { - mCurrentAnimation.setOnCancelRunnable(null); - } - mCurrentAnimation = mPendingAnimation.createPlaybackController() - .setOnCancelRunnable(this::clearState); // Setting this interpolator doesn't affect the visual motion, but is used to determine // whether we successfully reached the target state in onDragEnd(). mCurrentAnimation.getTarget().setInterpolator(currentInterpolator); @@ -303,27 +295,15 @@ public abstract class TaskViewTouchController animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity); } - mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd)); + mCurrentAnimation.setEndAction(this::clearState); mCurrentAnimation.startWithVelocity(mActivity, goingToEnd, velocity, mEndDisplacement, animationDuration); } - private void onCurrentAnimationEnd(boolean wasSuccess) { - if (mPendingAnimation != null) { - mPendingAnimation.finish(wasSuccess); - mPendingAnimation = null; - } - clearState(); - } - private void clearState() { mDetector.finishedScrolling(); mDetector.setDetectableScrollConditions(0, false); mTaskBeingDragged = null; mCurrentAnimation = null; - if (mPendingAnimation != null) { - mPendingAnimation.finish(false); - mPendingAnimation = null; - } } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index f35eb1fa5d..7efa5bd8c4 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -22,6 +22,7 @@ import static android.view.View.MeasureSpec.makeMeasureSpec; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; +import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; @@ -39,7 +40,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN; import static com.android.launcher3.statehandlers.DepthController.DEPTH; -import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; @@ -99,7 +99,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.anim.PendingAnimation.EndState; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -772,7 +771,7 @@ public abstract class RecentsView extends PagedView protected void applyLoadPlan(ArrayList tasks) { if (mPendingAnimation != null) { - mPendingAnimation.addEndListener((endState) -> applyLoadPlan(tasks)); + mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks)); return; } @@ -1464,19 +1463,10 @@ public abstract class RecentsView extends PagedView verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp); } - private void removeTask(TaskView taskView, int index, EndState endState) { - if (taskView.getTask() != null) { - UI_HELPER_EXECUTOR.execute(() -> - ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id)); - mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo()) - .log(LAUNCHER_TASK_DISMISS_SWIPE_UP); - } - } - public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView, boolean shouldRemoveTask, long duration) { if (mPendingAnimation != null) { - mPendingAnimation.finish(false); + mPendingAnimation.createPlaybackController().dispatchOnCancel(); } PendingAnimation anim = new PendingAnimation(duration); @@ -1559,22 +1549,27 @@ public abstract class RecentsView extends PagedView } mPendingAnimation = anim; - mPendingAnimation.addEndListener(new Consumer() { + mPendingAnimation.addEndListener(new Consumer() { @Override - public void accept(EndState endState) { - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && - taskView.isRunningTask() && endState.isSuccess) { - finishRecentsAnimation(true /* toHome */, () -> onEnd(endState)); + public void accept(Boolean success) { + if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask() && success) { + finishRecentsAnimation(true /* toHome */, () -> onEnd(success)); } else { - onEnd(endState); + onEnd(success); } } @SuppressWarnings("WrongCall") - private void onEnd(EndState endState) { - if (endState.isSuccess) { + private void onEnd(boolean success) { + if (success) { if (shouldRemoveTask) { - removeTask(taskView, draggedIndex, endState); + if (taskView.getTask() != null) { + UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() + .removeTask(taskView.getTask().key.id)); + mActivity.getStatsLogManager().logger() + .withItemInfo(taskView.getItemInfo()) + .log(LAUNCHER_TASK_DISMISS_SWIPE_UP); + } } int pageToSnapTo = mCurrentPage; @@ -1613,8 +1608,8 @@ public abstract class RecentsView extends PagedView } mPendingAnimation = anim; - mPendingAnimation.addEndListener((endState) -> { - if (endState.isSuccess) { + mPendingAnimation.addEndListener(isSuccess -> { + if (isSuccess) { // Remove all the task views now UI_HELPER_EXECUTOR.execute( ActivityManagerWrapper.getInstance()::removeAllRecentTasks); @@ -1642,7 +1637,6 @@ public abstract class RecentsView extends PagedView protected void runDismissAnimation(PendingAnimation pendingAnim) { AnimatorPlaybackController controller = pendingAnim.createPlaybackController(); controller.dispatchOnStart(); - controller.setEndAction(() -> pendingAnim.finish(true)); controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN); controller.start(); } @@ -2186,8 +2180,8 @@ public abstract class RecentsView extends PagedView mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator); mPendingAnimation.addOnFrameCallback(this::redrawLiveTile); } - mPendingAnimation.addEndListener((endState) -> { - if (endState.isSuccess) { + mPendingAnimation.addEndListener(isSuccess -> { + if (isSuccess) { Consumer onLaunchResult = (result) -> { onTaskLaunchAnimationEnd(result); if (!result) { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 5154018f6c..1b85b2ea8d 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -72,7 +72,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.statemanager.StatefulActivity; @@ -380,11 +379,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } public AnimatorPlaybackController createLaunchAnimationForRunningTask() { - final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation( - this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR); - AnimatorPlaybackController currentAnimation = pendingAnimation.createPlaybackController(); - currentAnimation.setEndAction(() -> pendingAnimation.finish(true)); - return currentAnimation; + return getRecentsView().createTaskLaunchAnimation( + this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR) + .createPlaybackController(); } public void launchTask(boolean animate) { diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index d9cf7f19b3..803f8d2c3d 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -16,6 +16,9 @@ package com.android.launcher3; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.graphics.drawable.Drawable; import android.util.FloatProperty; import android.util.IntProperty; @@ -29,8 +32,8 @@ public class LauncherAnimUtils { */ public static final int SPRING_LOADED_EXIT_DELAY = 500; - // The progress of an animation to all apps must be at least this far along to snap to all apps. - public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f; + // Progress after which the transition is assumed to be a success + public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; public static final IntProperty DRAWABLE_ALPHA = new IntProperty("drawableAlpha") { @@ -131,4 +134,23 @@ public class LauncherAnimUtils { return view.getAlpha(); } }; + + /** + * Utility method to create an {@link AnimatorListener} which executes a callback on animation + * cancel. + */ + public static AnimatorListener newCancelListener(Runnable callback) { + return new AnimatorListenerAdapter() { + + boolean mDispatched = false; + + @Override + public void onAnimationCancel(Animator animation) { + if (!mDispatched) { + mDispatched = true; + callback.run(); + } + } + }; + } } diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java index 08d573f08b..01d01ea35e 100644 --- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import android.annotation.TargetApi; import android.graphics.Insets; import android.os.Build; import android.util.Log; @@ -26,9 +27,8 @@ import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.os.BuildCompat; +import com.android.launcher3.Utilities; import com.android.launcher3.util.UiThreadHelper; /** @@ -68,7 +68,7 @@ public class AllAppsInsetTransitionController { } public void hide() { - if (!BuildCompat.isAtLeastR()) return; + if (!Utilities.ATLEAST_R) return; WindowInsets insets = mApps.getRootWindowInsets(); if (insets == null) return; @@ -89,9 +89,9 @@ public class AllAppsInsetTransitionController { * * @param progress value between 0..1 */ - @RequiresApi(api = Build.VERSION_CODES.R) + @TargetApi(Build.VERSION_CODES.R) public void onDragStart(float progress) { - if (!BuildCompat.isAtLeastR()) return; + if (!Utilities.ATLEAST_R) return; // Until getRootWindowInsets().isVisible(...) method returns correct value, // only support InsetController based IME transition during swipe up and @@ -164,7 +164,7 @@ public class AllAppsInsetTransitionController { * If IME bounds after touch sequence finishes, call finish. */ private boolean handleFinishOnFling(WindowInsetsAnimationController controller) { - if (!BuildCompat.isAtLeastR()) return false; + if (!Utilities.ATLEAST_R) return false; if (mState == State.FLING_END_TOP) { controller.finish(true); @@ -181,9 +181,9 @@ public class AllAppsInsetTransitionController { * * @param progress value between 0..1 */ - @RequiresApi(api = 30) + @TargetApi(Build.VERSION_CODES.R) public void setProgress(float progress) { - if (!BuildCompat.isAtLeastR()) return; + if (!Utilities.ATLEAST_R) return; // progress that equals to 0 or 1 is error prone. Do not use them. // Instead use onDragStart and onAnimationEnd if (mAnimationController == null || progress <= 0f || progress >= 1f) return; @@ -218,7 +218,7 @@ public class AllAppsInsetTransitionController { * * @param progress value between 0..1 */ - @RequiresApi(api = 30) + @TargetApi(Build.VERSION_CODES.R) public void onAnimationEnd(float progress) { if (DEBUG) { Log.d(TAG, "onAnimationEnd progress=" + progress diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index dcdfb6e51a..edaf51dbc6 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -29,8 +29,6 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; -import androidx.annotation.Nullable; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -72,7 +70,6 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL private Runnable mEndAction; protected boolean mTargetCancelled = false; - protected Runnable mOnCancelRunnable; /** package private */ AnimatorPlaybackController(AnimatorSet anim, long duration, ArrayList childAnims) { @@ -88,16 +85,11 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL @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 @@ -269,33 +261,6 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL } } - /** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */ - public void dispatchOnCancelWithoutCancelRunnable() { - dispatchOnCancelWithoutCancelRunnable(null); - } - - /** - * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This - * is intended to be used only if you need to cancel but want to defer cleaning up yourself. - * @param callback An optional callback to run after dispatching the cancel but before resetting - * the onCancelRunnable. - */ - public void dispatchOnCancelWithoutCancelRunnable(@Nullable Runnable callback) { - Runnable onCancel = mOnCancelRunnable; - setOnCancelRunnable(null); - dispatchOnCancel(); - if (callback != null) { - callback.run(); - } - setOnCancelRunnable(onCancel); - } - - - public AnimatorPlaybackController setOnCancelRunnable(Runnable runnable) { - mOnCancelRunnable = runnable; - return this; - } - public void dispatchOnStart() { callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart); } diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java index 6dd316ed60..4e90c9e67f 100644 --- a/src/com/android/launcher3/anim/PendingAnimation.java +++ b/src/com/android/launcher3/anim/PendingAnimation.java @@ -15,10 +15,12 @@ */ package com.android.launcher3.anim; +import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur; import android.animation.Animator; import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; @@ -43,8 +45,6 @@ import java.util.function.Consumer; */ public class PendingAnimation implements PropertySetter { - private final ArrayList> mEndListeners = new ArrayList<>(); - private final ArrayList mAnimHolders = new ArrayList<>(); private final AnimatorSet mAnim; private final long mDuration; @@ -73,13 +73,6 @@ public class PendingAnimation implements PropertySetter { addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders); } - public void finish(boolean isSuccess) { - for (Consumer listeners : mEndListeners) { - listeners.accept(new EndState(isSuccess)); - } - mEndListeners.clear(); - } - @Override public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { if (view == null || view.getAlpha() == alpha) { @@ -163,19 +156,38 @@ public class PendingAnimation implements PropertySetter { } /** - * Add a listener of receiving the end state. - * Note that the listeners are called as a result of calling {@link #finish(boolean)} - * and not automatically + * Add a listener of receiving the success/failure callback in the end. */ - public void addEndListener(Consumer listener) { - mEndListeners.add(listener); + public void addEndListener(Consumer listener) { + if (mProgressAnimator == null) { + mProgressAnimator = ValueAnimator.ofFloat(0, 1); + } + mProgressAnimator.addListener(new EndStateCallbackWrapper(listener)); } - public static class EndState { - public boolean isSuccess; + private static class EndStateCallbackWrapper extends AnimatorListenerAdapter { - public EndState(boolean isSuccess) { - this.isSuccess = isSuccess; + private final Consumer mListener; + private boolean mCalled = false; + + EndStateCallbackWrapper(Consumer listener) { + mListener = listener; + } + + @Override + public void onAnimationCancel(Animator animation) { + if (!mCalled) { + mCalled = true; + mListener.accept(false); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCalled) { + ValueAnimator anim = (ValueAnimator) animation; + mListener.accept(anim.getAnimatedFraction() > SUCCESS_TRANSITION_PROGRESS); + } } } } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 57c0ad92af..a9d0e614e4 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -15,7 +15,8 @@ */ package com.android.launcher3.touch; -import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS; +import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; +import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -32,6 +33,7 @@ import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; @@ -48,7 +50,6 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.StatsLogManager; @@ -64,9 +65,6 @@ import com.android.launcher3.util.TouchController; public abstract class AbstractStateChangeTouchController implements TouchController, SingleAxisSwipeDetector.Listener { - // Progress after which the transition is assumed to be a success in case user does not fling - public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; - /** * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this. */ @@ -77,6 +75,9 @@ public abstract class AbstractStateChangeTouchController protected final SingleAxisSwipeDetector mDetector; protected final SingleAxisSwipeDetector.Direction mSwipeDirection; + protected final AnimatorListener mClearStateOnCancelListener = + newCancelListener(this::clearState); + private boolean mNoIntercept; private boolean mIsLogContainerSet; protected int mStartContainerType; @@ -85,7 +86,7 @@ public abstract class AbstractStateChangeTouchController protected LauncherState mFromState; protected LauncherState mToState; protected AnimatorPlaybackController mCurrentAnimation; - protected PendingAnimation mPendingAnimation; + protected boolean mGoingBetweenStates = true; private float mStartProgress; // Ratio of transition process [0, 1] to drag displacement (px) @@ -209,7 +210,7 @@ public abstract class AbstractStateChangeTouchController mStartProgress = 0; mPassedOverviewAtomicThreshold = false; if (mCurrentAnimation != null) { - mCurrentAnimation.setOnCancelRunnable(null); + mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener); } int animComponents = goingBetweenNormalAndOverview(mFromState, mToState) ? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS; @@ -237,7 +238,7 @@ public abstract class AbstractStateChangeTouchController LauncherState toState) { return (fromState == NORMAL || fromState == OVERVIEW) && (toState == NORMAL || toState == OVERVIEW) - && mPendingAnimation == null; + && mGoingBetweenStates; } @Override @@ -412,9 +413,8 @@ public abstract class AbstractStateChangeTouchController ? mToState : mFromState; // snap to top or bottom using the release velocity } else { - float successProgress = mToState == ALL_APPS - ? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS; - targetState = (interpolatedProgress > successProgress) ? mToState : mFromState; + targetState = + (interpolatedProgress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; } final float endProgress; @@ -438,7 +438,8 @@ public abstract class AbstractStateChangeTouchController } else { // Let the state manager know that the animation didn't go to the target state, // but don't cancel ourselves (we already clean up when the animation completes). - mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(); + mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener); + mCurrentAnimation.dispatchOnCancel(); endProgress = 0; if (progress <= 0) { @@ -522,13 +523,7 @@ public abstract class AbstractStateChangeTouchController mAtomicComponentsController = null; } clearState(); - boolean shouldGoToTargetState = true; - if (mPendingAnimation != null) { - boolean reachedTarget = mToState == targetState; - mPendingAnimation.finish(reachedTarget); - mPendingAnimation = null; - shouldGoToTargetState = !reachedTarget; - } + boolean shouldGoToTargetState = mGoingBetweenStates || (mToState != targetState); if (shouldGoToTargetState) { goToTargetState(targetState); } @@ -568,6 +563,7 @@ public abstract class AbstractStateChangeTouchController mAtomicAnim.cancel(); mAtomicAnim = null; } + mGoingBetweenStates = true; mScheduleResumeAtomicComponent = false; mDetector.finishedScrolling(); mDetector.setDetectableScrollConditions(0, false);