[automerger] Reapply back button alpha at end of state animation am: 5273b695c2

Change-Id: Ic83348b0559743b334c5989d014987f144c390f1
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 308e92f..27de1e9 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 0674c61..114f96a 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -18,6 +18,8 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
+import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -31,8 +33,6 @@
 import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
-import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
-import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -53,9 +53,7 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.Log;
 import android.util.Pair;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -672,6 +670,10 @@
                     return;
                 }
 
+                if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
+                    mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
+                }
+
                 AnimatorSet anim = null;
                 RemoteAnimationProvider provider = mRemoteAnimationProvider;
                 if (provider != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 3fb7cd4..717179d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -25,7 +25,6 @@
 import android.animation.ValueAnimator;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
@@ -143,6 +142,8 @@
         TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
         if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
                 && taskView != null) {
+            // Reset the state manager, when changing the interaction mode
+            mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
             mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
             mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
 
@@ -190,7 +191,7 @@
                 // Update all apps interpolator to add a bit of overshoot starting from currFraction
                 final float currFraction = mCurrentAnimation.getProgressFraction();
                 mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
-                        new OvershootInterpolator(Math.min(Math.abs(velocity), 3f)), currFraction, 1);
+                        Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
                 animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
                         .setInterpolator(LINEAR);
             }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index dd5dcbe..ac9f863 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -25,7 +25,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
@@ -46,7 +45,6 @@
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityCompat;
@@ -171,7 +169,8 @@
         LauncherState state = launcher.getStateManager().getState();
         DeviceProfile profile = launcher.getDeviceProfile();
         WindowManagerWrapper.getInstance().setShelfHeight(
-                state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(),
+                (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+                        && !profile.isVerticalBarLayout(),
                 profile.hotseatBarSizePx);
 
         if (state == NORMAL) {
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 52a6dd5..0205c1f 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -15,7 +15,9 @@
  */
 package com.android.quickstep;
 
+import static android.view.View.TRANSLATION_Y;
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -25,6 +27,7 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
@@ -55,14 +58,15 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.TouchConsumer.InteractionType;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.views.LauncherLayoutListener;
-import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.RecentsViewContainer;
+import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.Objects;
@@ -243,34 +247,57 @@
             if (wasVisible) {
                 DeviceProfile dp = activity.getDeviceProfile();
                 long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-                activity.getStateManager().goToState(startState, false);
                 callback.accept(activity.getStateManager()
-                        .createAnimationToNewWorkspace(endState, accuracy));
+                        .createAnimationToNewWorkspace(startState, endState, accuracy));
                 return;
             }
 
-            if (activity.getDeviceProfile().isVerticalBarLayout()) {
-                return;
-            }
-
-            AllAppsTransitionController controller = activity.getAllAppsController();
             AnimatorSet anim = new AnimatorSet();
 
-            float scrollRange = Math.max(controller.getShiftRange(), 1);
-            float progressDelta = (transitionLength / scrollRange);
+            if (!activity.getDeviceProfile().isVerticalBarLayout()) {
+                AllAppsTransitionController controller = activity.getAllAppsController();
+                float scrollRange = Math.max(controller.getShiftRange(), 1);
+                float progressDelta = (transitionLength / scrollRange);
 
-            float endProgress = endState.getVerticalProgress(activity);
-            float startProgress = endProgress + progressDelta;
-            ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
-                    controller, ALL_APPS_PROGRESS, startProgress, endProgress);
-            shiftAnim.setInterpolator(LINEAR);
-            anim.play(shiftAnim);
+                float endProgress = endState.getVerticalProgress(activity);
+                float startProgress = endProgress + progressDelta;
+                ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
+                        controller, ALL_APPS_PROGRESS, startProgress, endProgress);
+                shiftAnim.setInterpolator(LINEAR);
+                anim.play(shiftAnim);
+            }
+
+            if (interactionType == INTERACTION_NORMAL) {
+                playScaleDownAnim(anim, activity);
+            }
 
             anim.setDuration(transitionLength * 2);
             activity.getStateManager().setCurrentAnimation(anim);
             callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2));
         }
 
+        /**
+         * Scale down recents from the center task being full screen to being in overview.
+         */
+        private void playScaleDownAnim(AnimatorSet anim, Launcher launcher) {
+            RecentsView recentsView = launcher.getOverviewPanel();
+            TaskView v = recentsView.getPageAt(recentsView.getCurrentPage());
+            ClipAnimationHelper clipHelper = new ClipAnimationHelper();
+            clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
+            if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
+                float fromScale = clipHelper.getSourceRect().width()
+                        / clipHelper.getTargetRect().width();
+                float fromTranslationY = clipHelper.getSourceRect().centerY()
+                        - clipHelper.getTargetRect().centerY();
+                Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, 1);
+                Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
+                        fromTranslationY, 0);
+                scale.setInterpolator(LINEAR);
+                translateY.setInterpolator(LINEAR);
+                anim.playTogether(scale, translateY);
+            }
+        }
+
         @Override
         public ActivityInitListener createActivityInitListener(
                 BiPredicate<Launcher, Boolean> onInitListener) {
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
index fbcde8b..336be2b 100644
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -20,11 +20,8 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
-import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
-import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
 
 import android.animation.ValueAnimator;
-import android.view.Surface;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
@@ -39,7 +36,6 @@
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Utility class to handle long swipe from an app.
@@ -65,7 +61,6 @@
     }
 
     private void init() {
-        setTargetAlpha(0, true);
         mFlingBlockCheck.blockFling();
 
         // Init animations
@@ -83,8 +78,7 @@
     }
 
     public void destroy() {
-        // TODO: We can probably also hide the task view
-        setTargetAlpha(1, false);
+        // TODO: We can probably also show the task view
 
         mLauncher.getStateManager().goToState(OVERVIEW, false);
     }
@@ -136,31 +130,6 @@
         animator.start();
     }
 
-    private void setTargetAlpha(float alpha, boolean defer) {
-        final Surface surface = getSurface(mLauncher.getDragLayer());
-        final long frameNumber = defer && surface != null ? getNextFrameNumber(surface) : -1;
-        if (defer) {
-            if (frameNumber == -1) {
-                defer = false;
-            } else {
-                mLauncher.getDragLayer().invalidate();
-            }
-        }
-
-        TransactionCompat transaction = new TransactionCompat();
-        for (RemoteAnimationTargetCompat app : mTargetSet.apps) {
-            if (!(app.isNotInRecents
-                    || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
-                transaction.setAlpha(app.leash, alpha);
-                if (defer) {
-                    transaction.deferTransactionUntil(app.leash, surface, frameNumber);
-                }
-            }
-        }
-        transaction.setEarlyWakeup();
-        transaction.apply();
-    }
-
     private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
         mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
         if (!toAllApps) {
@@ -176,4 +145,12 @@
 
         callback.run();
     }
+
+    public float getTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
+        if (!(app.isNotInRecents
+                || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
+            return 0;
+        }
+        return expectedAlpha;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 7a79c6f..e0089c6 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -108,7 +108,7 @@
                     } else {
                         mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
                                 LauncherLogProto.Action.Direction.NONE, page,
-                                TaskUtils.getComponentKeyForTask(taskView.getTask().key));
+                                TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key));
                     }
                     mWaitingForTaskLaunch = false;
                 }, taskView.getHandler());
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index ec2c318..c9ba7dc 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -75,8 +75,11 @@
             applicationInfo.loadLabel(packageManager), user);
     }
 
-    public static ComponentKey getComponentKeyForTask(Task.TaskKey taskKey) {
-        return new ComponentKey(taskKey.getComponent(), UserHandle.of(taskKey.userId));
+    public static ComponentKey getLaunchComponentKeyForTask(Task.TaskKey taskKey) {
+        final ComponentName cn = taskKey.sourceComponent != null
+                ? taskKey.sourceComponent
+                : taskKey.getComponent();
+        return new ComponentKey(cn, UserHandle.of(taskKey.userId));
     }
 
 
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index c94174b..66bc501 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -16,6 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -57,6 +58,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -84,6 +86,7 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.util.StringJoiner;
+import java.util.function.BiFunction;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
@@ -356,9 +359,9 @@
         // Override the visibility of the activity until the gesture actually starts and we swipe
         // up, or until we transition home and the home animation is composed
         if (alreadyOnHome) {
-            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
         } else {
-            mActivity.addForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+            mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
         }
 
         mRecentsView = activity.getOverviewPanel();
@@ -567,7 +570,8 @@
     }
 
     private void updateFinalShiftUi() {
-        if (mLauncherTransitionController == null) {
+        if (mLauncherTransitionController == null || mLauncherTransitionController
+                .getAnimationPlayer().isStarted()) {
             return;
         }
         mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
@@ -589,7 +593,15 @@
                     new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
             dp.updateInsets(homeContentInsets);
         } else {
-            overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+            if (mActivity != null) {
+                int loc[] = new int[2];
+                View rootView = mActivity.getRootView();
+                rootView.getLocationOnScreen(loc);
+                overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
+                        loc[1] + rootView.getHeight());
+            } else {
+                overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+            }
             // If we are not in multi-window mode, home insets should be same as system insets.
             Rect insets = new Rect();
             WindowManagerWrapper.getInstance().getStableInsets(insets);
@@ -632,7 +644,7 @@
         if (curActivity != null) {
             // Once the gesture starts, we can no longer transition home through the button, so
             // reset the force override of the activity visibility
-            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
         }
     }
 
@@ -653,17 +665,23 @@
     }
 
     private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
+        float velocityPxPerMs = endVelocity / 1000;
         long duration = MAX_SWIPE_DURATION;
         final float endShift;
         final float startShift;
+        final Interpolator interpolator;
         if (!isFling) {
             endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted ? 1 : 0;
             long expectedDuration = Math.abs(Math.round((endShift - mCurrentShift.value)
                     * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
             duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
             startShift = mCurrentShift.value;
+            interpolator = DEACCEL;
         } else {
             endShift = endVelocity < 0 ? 1 : 0;
+            interpolator = endVelocity < 0
+                    ? Interpolators.overshootInterpolatorForVelocity(velocityPxPerMs, 2f)
+                    : DEACCEL;
             float minFlingVelocity = mContext.getResources()
                     .getDimension(R.dimen.quickstep_fling_min_velocity);
             if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
@@ -672,14 +690,13 @@
                 // we want the page's snap velocity to approximately match the velocity at
                 // which the user flings, so we scale the duration by a value near to the
                 // derivative of the scroll interpolator at zero, ie. 2.
-                long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+                long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs));
                 duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
             }
-            startShift = Utilities.boundToRange(mCurrentShift.value - endVelocity * SINGLE_FRAME_MS
-                            / (mTransitionDragLength * 1000), 0, 1);
+            startShift = Utilities.boundToRange(mCurrentShift.value - velocityPxPerMs
+                    * SINGLE_FRAME_MS / (mTransitionDragLength), 0, 1);
         }
-
-        animateToProgress(startShift, endShift, duration, DEACCEL);
+        animateToProgress(startShift, endShift, duration, interpolator);
     }
 
     private void doLogGesture(boolean toLauncher) {
@@ -706,6 +723,12 @@
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
     private void animateToProgress(float start, float end, long duration,
             Interpolator interpolator) {
+        mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
+                interpolator));
+    }
+
+    private void animateToProgressInternal(float start, float end, long duration,
+            Interpolator interpolator) {
         mIsGoingToHome = Float.compare(end, 1) == 0;
         ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
         anim.setInterpolator(interpolator);
@@ -717,7 +740,26 @@
                         : STATE_SCALED_CONTROLLER_APP);
             }
         });
-        mRecentsAnimationWrapper.runOnInit(anim::start);
+        anim.start();
+        long startMillis = SystemClock.uptimeMillis();
+        executeOnUiThread(() -> {
+            // Animate the launcher components at the same time as the window, always on UI thread.
+            if (mLauncherTransitionController != null && !mWasLauncherAlreadyVisible
+                    && start != end && duration > 0) {
+                // Adjust start progress and duration in case we are on a different thread.
+                long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+                elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
+                float elapsedProgress = (float) elapsedMillis / duration;
+                float adjustedStart = Utilities.mapRange(elapsedProgress, start, end);
+                long adjustedDuration = duration - elapsedMillis;
+                // We want to use the same interpolator as the window, but need to adjust it to
+                // interpolate over the remaining progress (end - start).
+                mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
+                        interpolator, adjustedStart, end));
+                mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
+                mLauncherTransitionController.getAnimationPlayer().start();
+            }
+        });
     }
 
     @UiThread
@@ -769,6 +811,9 @@
     private void resetStateForAnimationCancel() {
         boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
         mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
+
+        // Leave the pending invisible flag, as it may be used by wallpaper open animation.
+        mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
     }
 
     public void layoutListenerClosed() {
@@ -915,6 +960,7 @@
 
         if (mLongSwipeController != null) {
             mLongSwipeController.destroy();
+            setTargetAlphaProvider((t, a1) -> a1);
 
             // Rebuild animations
             buildAnimationController();
@@ -956,6 +1002,7 @@
         mLongSwipeController = mActivityControlHelper.getLongSwipeController(
                 mActivity, mRecentsAnimationWrapper.targetSet);
         onLongSwipeDisplacementUpdated();
+        setTargetAlphaProvider(mLongSwipeController::getTargetAlpha);
     }
 
     private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
@@ -970,4 +1017,12 @@
                 () -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
 
     }
+
+    private void setTargetAlphaProvider(
+            BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
+        mClipAnimationHelper.setTaskAlphaCallback(provider);
+        // TODO: For some reason, when calling updateFinalShift multiple times on the same frame,
+        // only the first callback is executed.
+        Utilities.postAsyncCallback(mMainThreadHandler, this::updateFinalShift);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d550edc..1ea1a52 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -16,7 +16,7 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -199,7 +199,7 @@
         public void onPinnedStackAnimationStarted() {
             // Needed for activities that auto-enter PiP, which will not trigger a remote
             // animation to be created
-            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
         }
     };
 
@@ -667,9 +667,10 @@
 
             // The temporary running task is only used for the duration between the start of the
             // gesture and the task list is loaded and applied
-            mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(), 0, 0), null,
-                    null, "", "", 0, 0, false, true, false, false,
-                    new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false);
+            mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
+                    new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
+                    false, true, false, false, new ActivityManager.TaskDescription(), 0,
+                    new ComponentName("", ""), false);
             taskView.bind(mTmpRunningTask);
         }
         setCurrentTask(runningTaskId);
@@ -792,7 +793,7 @@
             if (shouldLog) {
                 mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                         onEndListener.logAction, Direction.UP, index,
-                        TaskUtils.getComponentKeyForTask(task.key));
+                        TaskUtils.getLaunchComponentKeyForTask(task.key));
             }
         }
     }
@@ -1229,7 +1230,7 @@
                 if (task != null) {
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                             onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
-                            TaskUtils.getComponentKeyForTask(task.key));
+                            TaskUtils.getLaunchComponentKeyForTask(task.key));
                 }
             } else {
                 onTaskLaunchFinish.accept(false);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b5f31b8..a7690e1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -118,7 +118,7 @@
             launchTask(true /* animate */);
             BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
-                    TaskUtils.getComponentKeyForTask(getTask().key));
+                    TaskUtils.getLaunchComponentKeyForTask(getTask().key));
         });
         setOutlineProvider(new TaskOutlineProvider(getResources()));
     }
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index b8e71a1..5c896e4 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল\'ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ\'ল"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ৱিজেট"</string>
-    <string name="widgets_list" msgid="796804551140113767">"ৱিজেটৰ তালিকা"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"ৱিজেটৰ তালিকা বন্ধ কৰা হ\'ল"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"গৃহ স্ক্ৰীণত যোগ কৰক"</string>
     <string name="action_move_here" msgid="2170188780612570250">"বস্তুটো ইয়ালৈ স্থানান্তৰ কৰক"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"বস্তুটো গৃহ স্ক্ৰীণত যোগ কৰা হ\'ল"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 70f0863..651f400 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনলোড হচ্ছে <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পন্ন হয়েছে"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টলের অপেক্ষায় রয়েছে"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> উইজেট"</string>
-    <string name="widgets_list" msgid="796804551140113767">"উইজেটের তালিকা"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"উইজেটের তালিকা বন্ধ করা হয়েছে"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"হোম স্ক্রীনে যোগ করুন"</string>
     <string name="action_move_here" msgid="2170188780612570250">"এখানে আইটেম সরান"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"হোম স্ক্রীনে আইটেম যোগ করা হয়েছে"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index d4f6bbb..6468d8b 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> વિજેટ"</string>
-    <string name="widgets_list" msgid="796804551140113767">"વિજેટની સૂચિ"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"વિજેટની સૂચિ બંધ કરવામાં આવી છે"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
     <string name="action_move_here" msgid="2170188780612570250">"આઇટમ અહીં ખસેડો"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"હોમ સ્ક્રીનમાં આઇટમ ઉમેરી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 44526b9..1b5ce26 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड हो रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> के इंस्टॉल होने की प्रतीक्षा की जा रही है"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
-    <string name="widgets_list" msgid="796804551140113767">"विजेट की सूची"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"विजेट की सूची बंद हो गई है"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"होम स्‍क्रीन में जोड़ें"</string>
     <string name="action_move_here" msgid="2170188780612570250">"आइटम यहां ले जाएं"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"होम स्क्रीन में आइटम जोड़ा गया"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 5182fd2..e73ff39 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಕಾಯಲಾಗುತ್ತಿದೆ"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ವಿಜೆಟ್‌ಗಳು"</string>
-    <string name="widgets_list" msgid="796804551140113767">"ವಿಜೆಟ್ ಪಟ್ಟಿ"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"ವಿಜೆಟ್ ಪಟ್ಟಿಯನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ಐಟಂ ಇಲ್ಲಿಗೆ ಸರಿಸಿ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ಮುಖಪುಟ ಪರದೆಗೆ ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index ed3bde6..d6a2fd1 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> വിജറ്റുകൾ"</string>
-    <string name="widgets_list" msgid="796804551140113767">"വിജറ്റുകളുടെ ലിസ്‌റ്റ്"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"വിജറ്റുകളുടെ ലിസ്‌റ്റ് അവസാനിപ്പിച്ചു"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ഹോം സ്ക്രീനിൽ ചേർക്കുക"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ഇനം ഇവിടേക്ക് നീക്കുക"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ഹോം സ്‌ക്രീനിൽ ഇനം ചേർത്തു"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 708a991..50c4279 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड गर्दै, <xliff:g id="PROGRESS">%2$s</xliff:g> सम्पन्‍न"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापना गर्न प्रतीक्षा गर्दै"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेटहरू"</string>
-    <string name="widgets_list" msgid="796804551140113767">"विजेटहरूको सूची"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"विजेटहरूको सूची बन्द गरियो"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"गृह स्क्रिनमा थप्नुहोस्"</string>
     <string name="action_move_here" msgid="2170188780612570250">"वस्तु यहाँ सार्नुहोस्"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"वस्तु गृह स्क्रिनमा थपियो"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index cb1cea9..5fa6607 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ଡାଉନଲୋଡ୍‌ ହେଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ ହେବାକୁ ଅପେକ୍ଷା କରିଛି"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ୱିଜେଟ୍‌"</string>
-    <string name="widgets_list" msgid="796804551140113767">"ୱିଜେଟ୍ ତାଲିକା"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"ୱିଜେଟ୍ ତାଲିକା ବନ୍ଦ ହୋଇଛି"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ହୋମ୍‌ ସ୍କ୍ରୀନରେ ଯୋଡ଼ନ୍ତୁ"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ଆଇଟମ୍‌କୁ ଏଠାକୁ ଘୁଞ୍ଚାନ୍ତୁ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ହୋମ୍‌ ସ୍କ୍ରୀନରେ ଆଇଟମ୍‌ ଯୋଡ଼ାଗଲା"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 1ca7cc8..3400e7b 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ਵਿਜੇਟ"</string>
-    <string name="widgets_list" msgid="796804551140113767">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index fedb0fd..983a0d1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>ஐப் பதிவிறக்குகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>ஐ நிறுவுவதற்காகக் காத்திருக்கிறது"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> விட்ஜெட்டுகள்"</string>
-    <string name="widgets_list" msgid="796804551140113767">"விட்ஜெட்கள் பட்டியல்"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"விட்ஜெட்கள் பட்டியல் மூடப்பட்டது"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"முகப்புத் திரையில் சேர்"</string>
     <string name="action_move_here" msgid="2170188780612570250">"இங்கு நகர்த்து"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3cc9761..a18d649 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్‌లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్‌స్టాల్ కావడానికి వేచి ఉంది"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> విడ్జెట్‌లు"</string>
-    <string name="widgets_list" msgid="796804551140113767">"విడ్జెట్‌ల జాబితా"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"విడ్జెట్‌ల జాబితా మూసివేయబడింది"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్‌కు జోడించు"</string>
     <string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్‌స్క్రీన్‌కి జోడించబడింది"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index c06c943..7853fd4 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -108,8 +108,10 @@
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ڈاؤن لوڈ ہو رہا ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گیا"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال ہونے کا انتظار کر رہی ہے"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ویجیٹس"</string>
-    <string name="widgets_list" msgid="796804551140113767">"ویجیٹس کی فہرست"</string>
-    <string name="widgets_list_closed" msgid="6141506579418771922">"ویجیٹس کی فہرست بند کر دی گئی"</string>
+    <!-- no translation found for widgets_list (796804551140113767) -->
+    <skip />
+    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
+    <skip />
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ہوم اسکرین میں شامل کریں"</string>
     <string name="action_move_here" msgid="2170188780612570250">"آئٹم یہاں منتقل کریں"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"آئٹم کو ہوم اسکرین میں شامل کر دیا گیا"</string>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index e117deb..a4b6f5b 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -44,13 +44,25 @@
 
     public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
     public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
+    public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2;
+
+    // This is not treated as invisibility flag, but adds as a hint for an incomplete transition.
+    // When the wallpaper animation runs, it replaces this flag with a proper invisibility
+    // flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation.
+    public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3;
+
+    private static final int INVISIBLE_FLAGS =
+            INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS;
+    public static final int STATE_HANDLER_INVISIBILITY_FLAGS =
+            INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
     public static final int INVISIBLE_ALL =
-            INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS;
+            INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
 
     @Retention(SOURCE)
     @IntDef(
             flag = true,
-            value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS})
+            value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS,
+                    INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION})
     public @interface InvisibilityFlags{}
 
     private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
@@ -208,7 +220,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
-     * @see LauncherAppTransitionManagerImpl.getWallpaperOpenRunner()
+     * @see LauncherAppTransitionManagerImpl#getWallpaperOpenRunner()
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
         mForceInvisible |= flag;
@@ -218,12 +230,15 @@
         mForceInvisible &= ~flag;
     }
 
-
     /**
      * @return Wether this activity should be considered invisible regardless of actual visibility.
      */
     public boolean isForceInvisible() {
-        return mForceInvisible != 0;
+        return hasSomeInvisibleFlag(INVISIBLE_FLAGS);
+    }
+
+    public boolean hasSomeInvisibleFlag(int mask) {
+        return (mForceInvisible & mask) != 0;
     }
 
     public interface MultiWindowModeChangedListener {
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 403c8b8..4e0f2e7 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -99,6 +99,10 @@
         mShowImeAfterFirstLayout = !showSoftInput();
     }
 
+    public void hideKeyboard() {
+        UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
+    }
+
     private boolean showSoftInput() {
         return requestFocus() &&
                 ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
@@ -106,7 +110,7 @@
     }
 
     public void dispatchBackKey() {
-        UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
+        hideKeyboard();
         if (mBackKeyListener != null) {
             mBackKeyListener.onBackKey();
         }
@@ -135,6 +139,6 @@
                 nextFocus.requestFocus();
             }
         }
-        UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
+        hideKeyboard();
     }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 5b010dc..8a15b24 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -242,8 +242,10 @@
      * Called when the start transition ends and the user settles on this particular state.
      */
     public void onStateTransitionEnd(Launcher launcher) {
-        if (this == NORMAL) {
+        if (this == NORMAL || this == SPRING_LOADED) {
             UiFactory.resetOverview(launcher);
+        }
+        if (this == NORMAL) {
             // Clear any rotation locks when going to normal state
             launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
         }
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 05c515b..8b415d6 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -298,6 +298,24 @@
 
     /**
      * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
+     * state transition. The UI is force-set to fromState before creating the controller.
+     * @param fromState the initial state for the transition.
+     * @param state the final state for the transition.
+     * @param duration intended duration for normal playback. Use higher duration for better
+     *                accuracy.
+     */
+    public AnimatorPlaybackController createAnimationToNewWorkspace(
+            LauncherState fromState, LauncherState state, long duration) {
+        mConfig.reset();
+        for (StateHandler handler : getStateHandlers()) {
+            handler.setState(fromState);
+        }
+
+        return createAnimationToNewWorkspace(state, duration);
+    }
+
+    /**
+     * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
      * state transition.
      * @param state the final state for the transition.
      * @param duration intended duration for normal playback. Use higher duration for better
@@ -348,12 +366,6 @@
             }
 
             @Override
-            public void onAnimationCancel(Animator animation) {
-                super.onAnimationCancel(animation);
-                mState = mCurrentStableState;
-            }
-
-            @Override
             public void onAnimationSuccess(Animator animator) {
                 // Run any queued runnables
                 if (onCompleteRunnable != null) {
@@ -438,6 +450,7 @@
     }
 
     public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
+        clearCurrentAnimation();
         setCurrentAnimation(controller.getTarget());
         mConfig.userControlled = true;
         mConfig.playbackController = controller;
@@ -538,6 +551,9 @@
 
         @Override
         public void onAnimationEnd(Animator animation) {
+            if (playbackController != null && playbackController.getTarget() == animation) {
+                playbackController = null;
+            }
             if (mCurrentAnimation == animation) {
                 mCurrentAnimation = null;
             }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 4bd9a9b..5355c5e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -272,6 +272,24 @@
         return scale;
     }
 
+    /**
+     * Maps t from one range to another range.
+     * @param t The value to map.
+     * @param fromMin The lower bound of the range that t is being mapped from.
+     * @param fromMax The upper bound of the range that t is being mapped from.
+     * @param toMin The lower bound of the range that t is being mapped to.
+     * @param toMax The upper bound of the range that t is being mapped to.
+     * @return The mapped value of t.
+     */
+    public static float mapToRange(float t, float fromMin, float fromMax, float toMin, float toMax) {
+        if (fromMin == fromMax || toMin == toMax) {
+            Log.e(TAG, "mapToRange: range has 0 length");
+            return toMin;
+        }
+        float progress = Math.abs(t - fromMin) / Math.abs(fromMax - fromMin);
+        return mapRange(progress, toMin, toMax);
+    }
+
     public static float mapRange(float value, float min, float max) {
         return min + (value * (max - min));
     }
@@ -463,6 +481,13 @@
     }
 
     /**
+     * @see #boundToRange(int, int, int).
+     */
+    public static long boundToRange(long value, long lowerBound, long upperBound) {
+        return Math.max(lowerBound, Math.min(value, upperBound));
+    }
+
+    /**
      * Wraps a message with a TTS span, so that a different message is spoken than
      * what is getting displayed.
      * @param msg original message
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index e83904f..dcc4554 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -21,6 +21,8 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
 import android.view.inputmethod.EditorInfo;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
@@ -37,7 +39,8 @@
  * An interface to a search box that AllApps can command.
  */
 public class AllAppsSearchBarController
-        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
+        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
+        OnFocusChangeListener {
 
     protected Launcher mLauncher;
     protected Callbacks mCb;
@@ -62,6 +65,7 @@
         mInput.addTextChangedListener(this);
         mInput.setOnEditorActionListener(this);
         mInput.setOnBackKeyListener(this);
+        mInput.setOnFocusChangeListener(this);
         mSearchAlgorithm = searchAlgorithm;
     }
 
@@ -123,6 +127,13 @@
         return false;
     }
 
+    @Override
+    public void onFocusChange(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            mInput.hideKeyboard();
+        }
+    }
+
     /**
      * Resets the search bar state.
      */
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 84085cb..50fb0a5 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -202,6 +202,19 @@
         }
     }
 
+    public void dispatchSetInterpolator(TimeInterpolator interpolator) {
+        dispatchSetInterpolatorRecursively(mAnim, interpolator);
+    }
+
+    private void dispatchSetInterpolatorRecursively(Animator anim, TimeInterpolator interpolator) {
+        anim.setInterpolator(interpolator);
+        if (anim instanceof AnimatorSet) {
+            for (Animator child : nonNullList(((AnimatorSet) anim).getChildAnimations())) {
+                dispatchSetInterpolatorRecursively(child, interpolator);
+            }
+        }
+    }
+
     public void setOnCancelRunnable(Runnable runnable) {
         mOnCancelRunnable = runnable;
     }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index bace7df..d17572e 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -24,6 +24,8 @@
 import android.view.animation.OvershootInterpolator;
 import android.view.animation.PathInterpolator;
 
+import com.android.launcher3.Utilities;
+
 
 /**
  * Common interpolators used in Launcher
@@ -116,6 +118,19 @@
         return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
     }
 
+    public static Interpolator overshootInterpolatorForVelocity(float velocity) {
+        return overshootInterpolatorForVelocity(velocity, 1f);
+    }
+
+    /**
+     * Create an OvershootInterpolator with tension directly related to the velocity (in px/ms).
+     * @param velocity The start velocity of the animation we want to overshoot.
+     * @param dampFactor An optional factor to reduce the amount of tension (how far we overshoot).
+     */
+    public static Interpolator overshootInterpolatorForVelocity(float velocity, float dampFactor) {
+        return new OvershootInterpolator(Math.min(Math.abs(velocity), 3f) / dampFactor);
+    }
+
     /**
      * Runs the given interpolator such that the entire progress is set between the given bounds.
      * That is, we set the interpolation to 0 until lowerBound and reach 1 by upperBound.
@@ -135,4 +150,15 @@
             return interpolator.getInterpolation((t - lowerBound) / (upperBound - lowerBound));
         };
     }
+
+    /**
+     * Runs the given interpolator such that the interpolated value is mapped to the given range.
+     * This is useful, for example, if we only use this interpolator for part of the animation,
+     * such as to take over a user-controlled animation when they let go.
+     */
+    public static Interpolator mapToProgress(Interpolator interpolator, float lowerBound,
+            float upperBound) {
+        return t -> Utilities.mapToRange(interpolator.getInterpolation(t), 0, 1,
+                lowerBound, upperBound);
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 42ba191..d3a7955 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -77,7 +77,7 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final PathMeasure mPathMeasure = new PathMeasure();
 
-    private final Context mContext;
+    private final ItemInfoWithIcon mItem;
 
     // Path in [0, 100] bounds.
     private final Path mProgressPath;
@@ -106,7 +106,7 @@
      */
     public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
         super(info);
-        mContext = context;
+        mItem = info;
         mProgressPath = progressPath;
         mScaledTrackPath = new Path();
         mScaledProgressPath = new Path();
@@ -274,7 +274,7 @@
             mTrackAlpha = MAX_PAINT_ALPHA;
             setIsDisabled(true);
         } else if (progress >= 1) {
-            setIsDisabled(false);
+            setIsDisabled(mItem.isDisabled());
             mScaledTrackPath.set(mScaledProgressPath);
             float fraction = (progress - 1) / COMPLETE_ANIM_FRACTION;
 
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 089303e..977dcd7 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -42,6 +41,9 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.BitmapInfo;
 import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LongArrayMap;
@@ -52,6 +54,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Handles updates due to changes in package manager (app installed/updated/removed)
@@ -162,12 +165,7 @@
 
         final ArrayMap<ComponentName, AppInfo> addedOrUpdatedApps = new ArrayMap<>();
         if (!addedOrModified.isEmpty()) {
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindAppsAddedOrUpdated(addedOrModified);
-                }
-            });
+            scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
             for (AppInfo ai : addedOrModified) {
                 addedOrUpdatedApps.put(ai.componentName, ai);
             }
@@ -213,11 +211,26 @@
                             }
 
                             if (si.isPromise() && isNewApkAvailable) {
+                                boolean isTargetValid = true;
+                                if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                    List<ShortcutInfoCompat> shortcut = DeepShortcutManager
+                                            .getInstance(context).queryForPinnedShortcuts(
+                                                    cn.getPackageName(),
+                                                    Arrays.asList(si.getDeepShortcutId()), mUser);
+                                    if (shortcut.isEmpty()) {
+                                        isTargetValid = false;
+                                    } else {
+                                        si.updateFromDeepShortcutInfo(shortcut.get(0), context);
+                                        infoUpdated = true;
+                                    }
+                                } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
+                                    isTargetValid = LauncherAppsCompat.getInstance(context)
+                                            .isActivityEnabledForProfile(cn, mUser);
+                                }
+
                                 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
                                     // Auto install icon
-                                    LauncherAppsCompat launcherApps
-                                            = LauncherAppsCompat.getInstance(context);
-                                    if (!launcherApps.isActivityEnabledForProfile(cn, mUser)) {
+                                    if (!isTargetValid) {
                                         // Try to find the best match activity.
                                         Intent intent = new PackageManagerHelper(context)
                                                 .getAppLaunchIntent(cn.getPackageName(), mUser);
@@ -235,6 +248,11 @@
                                             continue;
                                         }
                                     }
+                                } else if (!isTargetValid) {
+                                    removedShortcuts.put(si.id, true);
+                                    FileLog.e(TAG, "Restored shortcut no longer valid "
+                                            + si.intent);
+                                    continue;
                                 } else {
                                     si.status = ShortcutInfo.DEFAULT;
                                     infoUpdated = true;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index f44f5c8..24e2e2f 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -181,7 +181,12 @@
      * If packageName is null, returns all pinned shortcuts regardless of package.
      */
     public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
-        return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, null, user);
+        return queryForPinnedShortcuts(packageName, null, user);
+    }
+
+    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+            List<String> shortcutIds, UserHandle user) {
+        return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
     }
 
     public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 453810c..898b1b7 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -86,6 +86,9 @@
     private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
 
     private AnimatorSet mAtomicAnim;
+    // True if we want to resume playing atomic components when mAtomicAnim completes.
+    private boolean mScheduleResumeAtomicComponent;
+
     private boolean mPassedOverviewAtomicThreshold;
     // mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
     // However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
@@ -93,6 +96,8 @@
     // interfere with the atomic animation. When the atomic animation ends, we start controlling
     // the atomic components as well, using this controller.
     private AnimatorPlaybackController mAtomicComponentsController;
+    private LauncherState mAtomicComponentsTargetState = NORMAL;
+
     private float mAtomicComponentsStartProgress;
 
     public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
@@ -191,27 +196,21 @@
         }
         int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
                 ? NON_ATOMIC_COMPONENT : ANIM_ALL;
+        mScheduleResumeAtomicComponent = false;
         if (mAtomicAnim != null) {
+            animComponents = NON_ATOMIC_COMPONENT;
             // Control the non-atomic components until the atomic animation finishes, then control
             // the atomic components as well.
-            animComponents = NON_ATOMIC_COMPONENT;
-            mAtomicAnim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animation) {
-                    cancelAtomicComponentsController();
-                    if (mCurrentAnimation != null) {
-                        mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
-                        long duration = (long) (getShiftRange() * 2);
-                        mAtomicComponentsController = AnimatorPlaybackController.wrap(
-                                createAtomicAnimForState(mFromState, mToState, duration), duration);
-                        mAtomicComponentsController.dispatchOnStart();
-                    }
-                }
-            });
+            mScheduleResumeAtomicComponent = true;
         }
-        if (goingBetweenNormalAndOverview(mFromState, mToState)) {
+        if (goingBetweenNormalAndOverview(mFromState, mToState)
+                || mAtomicComponentsTargetState != mToState) {
             cancelAtomicComponentsController();
         }
+
+        if (mAtomicComponentsController != null) {
+            animComponents &= ~ATOMIC_COMPONENT;
+        }
         mProgressMultiplier = initCurrentAnimation(animComponents);
         mCurrentAnimation.dispatchOnStart();
         return true;
@@ -302,10 +301,28 @@
                 mAtomicAnim.cancel();
             }
             mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
-            mAtomicAnim.addListener(new AnimatorListenerAdapter() {
+            mAtomicAnim.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
                     mAtomicAnim = null;
+                    mScheduleResumeAtomicComponent = false;
+                }
+
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    if (!mScheduleResumeAtomicComponent) {
+                        return;
+                    }
+                    cancelAtomicComponentsController();
+                    if (mCurrentAnimation != null) {
+                        mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
+                        long duration = (long) (getShiftRange() * 2);
+                        mAtomicComponentsController = AnimatorPlaybackController.wrap(
+                                createAtomicAnimForState(mFromState, mToState, duration), duration);
+                        mAtomicComponentsController.dispatchOnStart();
+                        mAtomicComponentsTargetState = mToState;
+                    }
                 }
             });
             mAtomicAnim.start();
@@ -457,7 +474,7 @@
     }
 
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        clearState();
+        cancelAnimationControllers();
         boolean shouldGoToTargetState = true;
         if (mPendingAnimation != null) {
             boolean reachedTarget = mToState == targetState;
@@ -484,6 +501,15 @@
     }
 
     protected void clearState() {
+        cancelAnimationControllers();
+        if (mAtomicAnim != null) {
+            mAtomicAnim.cancel();
+            mAtomicAnim = null;
+        }
+        mScheduleResumeAtomicComponent = false;
+    }
+
+    private void cancelAnimationControllers() {
         mCurrentAnimation = null;
         cancelAtomicComponentsController();
         mDetector.finishedScrolling();