From 649903838e0376bdc5d98c2d0c487d3c9acdf20a Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Tue, 9 Apr 2024 19:32:12 -0700 Subject: [PATCH] Cancel manual animation if LauncherState changes during drag. Previously, the following would cause the All Apps panel to appear in NORMAL state: 1. Start dragging to all apps 2. During the drag, something sets Launcher to NORMAL 3. Release finger -> animation to all apps completes, but state is still NORMAL Side effects of this: - On large screens, All Apps draws its background on Launcher's ScrimView only if the current state is All Apps. So in this case, the apps just floated above the workspace. - On handheld, touches are handled by workspace even though you can see the All Apps list. So e.g. if you swipe down, the notification shade appears rather than all apps panel hiding (although it seems this touch issue was addressed separately). I'm not sure if this is the main/only case of this state mismatch happening, but verified with local async state changes that this could in theory happen. We haven't been able to organically repro the bug reliably. That being said, it feels plausible that a well timed screen lock during the all apps transition could also hit this case. Demo videos with hard-coded state change to NORMAL 2 seconds after you start swiping up to all apps (note I release my finger at the end of each video): https://drive.google.com/drive/folders/1ul8ep9N2M5oc6ZSbf_ZHQwp9IwTpz7GB?resourcekey=0-4LAufl0rkvtjvgZC0L-eMQ&usp=drive_link Bug: 239394946 Bug: 331600490 Test: Manual with local async launcher state changes Flag: NA (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:22dee942b595acae7a804b3008d9b732dad42620) Merged-In: I6364dbde8aea67f5d1c525edf57ed7eb26096cf9 Change-Id: I6364dbde8aea67f5d1c525edf57ed7eb26096cf9 --- .../NavBarToHomeTouchController.java | 4 ++-- ...ButtonNavbarToOverviewTouchController.java | 4 ++-- .../NoButtonQuickSwitchTouchController.java | 2 +- .../android/launcher3/LauncherAnimUtils.java | 19 +++++++++++++++---- .../AbstractStateChangeTouchController.java | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index e8b508168d..3ed2d0bdb8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -19,7 +19,7 @@ import static com.android.app.animation.Interpolators.DECELERATE_3; 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.LauncherAnimUtils.newSingleUseCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; @@ -165,7 +165,7 @@ public class NavBarToHomeTouchController implements TouchController, topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder); } mCurrentAnimation = builder.createPlaybackController(); - mCurrentAnimation.getTarget().addListener(newCancelListener(this::clearState)); + mCurrentAnimation.getTarget().addListener(newSingleUseCancelListener(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 26e994f4e4..42be52f7c4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -18,7 +18,7 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE; import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; -import static com.android.launcher3.LauncherAnimUtils.newCancelListener; +import static com.android.launcher3.LauncherAnimUtils.newSingleUseCancelListener; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; @@ -226,7 +226,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch return; } mNormalToHintOverviewScrimAnimator = null; - mCurrentAnimation.getTarget().addListener(newCancelListener(() -> + mCurrentAnimation.getTarget().addListener(newSingleUseCancelListener(() -> mLauncher.getStateManager().goToState(OVERVIEW, true, forSuccessCallback(() -> { mOverviewResistYAnim = AnimatorControllerWithResistance .createRecentsResistanceFromOverviewAnim(mLauncher, null) diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 3a1c42dc89..47e12fa6de 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -111,7 +111,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, private final float mMotionPauseMinDisplacement; private final LauncherRecentsView mRecentsView; protected final AnimatorListener mClearStateOnCancelListener = - newCancelListener(this::clearState); + newCancelListener(this::clearState, /* isSingleUse = */ false); private boolean mNoIntercept; private LauncherState mStartState; diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index c20f323055..6a9d170b2c 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -220,17 +220,28 @@ public class LauncherAnimUtils { /** * Utility method to create an {@link AnimatorListener} which executes a callback on animation - * cancel. + * cancel. Once the cancel has been dispatched, this listener will no longer be called. */ - public static AnimatorListener newCancelListener(Runnable callback) { - return new AnimatorListenerAdapter() { + public static AnimatorListener newSingleUseCancelListener(Runnable callback) { + return newCancelListener(callback, true); + } + /** + * Utility method to create an {@link AnimatorListener} which executes a callback on animation + * cancel. + * + * @param isSingleUse {@code true} means the callback will be called at most once + */ + public static AnimatorListener newCancelListener(Runnable callback, boolean isSingleUse) { + return new AnimatorListenerAdapter() { boolean mDispatched = false; @Override public void onAnimationCancel(Animator animation) { if (!mDispatched) { - mDispatched = true; + if (isSingleUse) { + mDispatched = true; + } callback.run(); } } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 9aed4ebb51..50f98f297b 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -57,7 +57,7 @@ public abstract class AbstractStateChangeTouchController protected final SingleAxisSwipeDetector.Direction mSwipeDirection; protected final AnimatorListener mClearStateOnCancelListener = - newCancelListener(this::clearState); + newCancelListener(this::clearState, /* isSingleUse = */ false); private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck(); protected int mStartContainerType;