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
This commit is contained in:
Andy Wickham
2024-04-09 19:32:12 -07:00
committed by Cherrypicker Worker
parent eaf9f083dc
commit 649903838e
5 changed files with 21 additions and 10 deletions

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;