[automerger skipped] Merge Android 12L am: bac85d0348 -s ours am: 5f2987a89b -s ours

am skip reason: Merged-In Icf0cc432ab3a97614b27efcf6fc9d18ea423a743 with SHA-1 11a0454879 is already in history

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/2013193

Change-Id: I2881c5cf413f745e6817f4dde07ec4c04ff033c7
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4269f2d..4ceb195 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -39,6 +39,7 @@
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.view.View;
 import android.view.WindowInsets;
 import android.window.SplashScreen;
@@ -513,11 +514,26 @@
     }
 
     /**
-     * Adds a new launch cookie for the activity launch of the given {@param info} if supported.
+     * Adds a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     * @param opts the options to set the launchCookie on.
      */
     public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
+        IBinder launchCookie = getLaunchCookie(info);
+        if (launchCookie != null) {
+            opts.setLaunchCookie(launchCookie);
+        }
+    }
+
+    /**
+     * Return a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     */
+    public IBinder getLaunchCookie(ItemInfo info) {
         if (info == null) {
-            return;
+            return null;
         }
         switch (info.container) {
             case LauncherSettings.Favorites.CONTAINER_DESKTOP:
@@ -531,8 +547,7 @@
                     break;
                 }
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -543,10 +558,9 @@
                 break;
             default:
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
-        opts.setLaunchCookie(ObjectWrapper.wrap(new Integer(info.id)));
+        return ObjectWrapper.wrap(new Integer(info.id));
     }
 
     public void setHintUserWillBeActive() {
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 6e2fadd..49b2cc5 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -367,7 +367,7 @@
         if (launcherClosing) {
             // Delay animation by a frame to avoid jank.
             Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                    getLauncherContentAnimator(true /* isAppOpening */, startDelay);
+                    getLauncherContentAnimator(true /* isAppOpening */, startDelay, false);
             anim.play(launcherContentAnimator.first);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -464,9 +464,10 @@
      * @param isAppOpening True when this is called when an app is opening.
      *                     False when this is called when an app is closing.
      * @param startDelay   Start delay duration.
+     * @param skipAllAppsScale True if we want to avoid scaling All Apps
      */
     private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
-            int startDelay) {
+            int startDelay, boolean skipAllAppsScale) {
         AnimatorSet launcherAnimator = new AnimatorSet();
         Runnable endListener;
 
@@ -484,7 +485,6 @@
             final float startAlpha = appsView.getAlpha();
             final float startScale = SCALE_PROPERTY.get(appsView);
             appsView.setAlpha(alphas[0]);
-            SCALE_PROPERTY.set(appsView, scales[0]);
 
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
             alpha.setDuration(CONTENT_ALPHA_DURATION);
@@ -496,12 +496,16 @@
                     appsView.setLayerType(View.LAYER_TYPE_NONE, null);
                 }
             });
-            ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
-            scale.setInterpolator(AGGRESSIVE_EASE);
-            scale.setDuration(CONTENT_SCALE_DURATION);
+
+            if (!skipAllAppsScale) {
+                SCALE_PROPERTY.set(appsView, scales[0]);
+                ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
+                scale.setInterpolator(AGGRESSIVE_EASE);
+                scale.setDuration(CONTENT_SCALE_DURATION);
+                launcherAnimator.play(scale);
+            }
 
             launcherAnimator.play(alpha);
-            launcherAnimator.play(scale);
 
             endListener = () -> {
                 appsView.setAlpha(startAlpha);
@@ -1565,6 +1569,7 @@
                         || mLauncher.getWorkspace().isOverlayShown();
 
                 boolean playWorkspaceReveal = true;
+                boolean skipAllAppsScale = false;
                 if (mFromUnlock) {
                     anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
                 } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
@@ -1579,6 +1584,10 @@
                                 true /* animateOverviewScrim */, launcherView).getAnimators());
                         // We play StaggeredWorkspaceAnim as a part of the closing window animation.
                         playWorkspaceReveal = false;
+                    } else {
+                        // Skip scaling all apps, otherwise FloatingIconView will get wrong
+                        // layout bounds.
+                        skipAllAppsScale = true;
                     }
                 } else {
                     anim.play(getFallbackClosingWindowAnimators(appTargets));
@@ -1600,7 +1609,8 @@
 
                     if (mLauncher.isInState(LauncherState.ALL_APPS)) {
                         Pair<AnimatorSet, Runnable> contentAnimator =
-                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY);
+                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
+                                        skipAllAppsScale);
                         anim.play(contentAnimator.first);
                         anim.addListener(new AnimatorListenerAdapter() {
                             @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5308a8f..3b6d5fb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -90,11 +90,12 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
-    private final DeviceProfile mDeviceProfile;
     private final LayoutInflater mLayoutInflater;
     private final TaskbarDragLayer mDragLayer;
     private final TaskbarControllers mControllers;
 
+    private DeviceProfile mDeviceProfile;
+
     private final WindowManager mWindowManager;
     private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
     private final int mTaskbarHeightForIme;
@@ -125,10 +126,7 @@
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
 
         final Resources resources = getResources();
-        float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
-        mDeviceProfile.updateIconSize(1, resources);
-        float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
-        mDeviceProfile.updateIconSize(iconScale, resources);
+        updateIconSize(resources);
 
         mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
 
@@ -211,6 +209,19 @@
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
     }
 
+    /** Updates the Device profile instance to the latest representation of the screen. */
+    public void updateDeviceProfile(DeviceProfile dp) {
+        mDeviceProfile = dp;
+        updateIconSize(getResources());
+    }
+
+    private void updateIconSize(Resources resources) {
+        float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
+        mDeviceProfile.updateIconSize(1, resources);
+        float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
+        mDeviceProfile.updateIconSize(iconScale, resources);
+    }
+
     public void onConfigurationChanged(@Config int configChanges) {
         mControllers.onConfigurationChanged(configChanges);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index b3a9f8d..21d7411 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -145,7 +145,7 @@
             PopupContainerWithArrow<TaskbarActivityContext> popupContainer =
                     mControllers.taskbarPopupController.showForIcon(btv);
             if (popupContainer != null) {
-                dragOptions.preDragCondition = popupContainer.createPreDragCondition();
+                dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 7a50d0b..be5ab55 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -207,59 +207,10 @@
 
     private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
         AnimatorSet animatorSet = new AnimatorSet();
-        if (hasAnyFlag(changedFlags, FLAG_RESUMED)) {
-            boolean isResumed = isResumed();
-            ObjectAnimator anim = mIconAlignmentForResumedState
-                    .animateToValue(isResumed && goingToUnstashedLauncherState()
-                            ? 1 : 0)
-                    .setDuration(duration);
 
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mIsAnimatingToLauncherViaResume = false;
-                }
-
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    mIsAnimatingToLauncherViaResume = isResumed;
-
-                    TaskbarStashController stashController = mControllers.taskbarStashController;
-                    stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
-                    stashController.applyState(duration);
-                }
-            });
-            animatorSet.play(anim);
-        }
-
-        if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) {
-            boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
-            Animator animator = mIconAlignmentForGestureState
-                    .animateToValue(isRecentsAnimationRunning && goingToUnstashedLauncherState()
-                            ? 1 : 0);
-            if (isRecentsAnimationRunning) {
-                animator.setDuration(duration);
-            }
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mIsAnimatingToLauncherViaGesture = false;
-                }
-
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
-                }
-            });
-            animatorSet.play(animator);
-        }
-
-        if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
-            boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
-            animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
-                    .setDuration(duration));
-        }
-
+        // Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can
+        // determine whether goingToUnstashedLauncherStateChanged.
+        boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState();
         if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
             boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
             playStateTransitionAnim(animatorSet, duration, committed);
@@ -270,6 +221,76 @@
                 applyState(0 /* duration */);
             }
         }
+        boolean goingToUnstashedLauncherStateChanged = wasGoingToUnstashedLauncherState
+                != goingToUnstashedLauncherState();
+
+        boolean launcherStateChangedDuringAnimToResumeAlignment =
+                mIconAlignmentForResumedState.isAnimating() && goingToUnstashedLauncherStateChanged;
+        if (hasAnyFlag(changedFlags, FLAG_RESUMED)
+                || launcherStateChangedDuringAnimToResumeAlignment) {
+            boolean isResumed = isResumed();
+            float toAlignmentForResumedState = isResumed && goingToUnstashedLauncherState() ? 1 : 0;
+            // If we're already animating to the value, just leave it be instead of restarting it.
+            if (!mIconAlignmentForResumedState.isAnimatingToValue(toAlignmentForResumedState)) {
+                ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
+                        .animateToValue(toAlignmentForResumedState)
+                        .setDuration(duration);
+
+                resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mIsAnimatingToLauncherViaResume = false;
+                    }
+
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        mIsAnimatingToLauncherViaResume = isResumed;
+
+                        TaskbarStashController stashController =
+                                mControllers.taskbarStashController;
+                        stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
+                        stashController.applyState(duration);
+                    }
+                });
+                animatorSet.play(resumeAlignAnim);
+            }
+        }
+
+
+        boolean launcherStateChangedDuringAnimToGestureAlignment =
+                mIconAlignmentForGestureState.isAnimating() && goingToUnstashedLauncherStateChanged;
+        if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)
+                || launcherStateChangedDuringAnimToGestureAlignment) {
+            boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
+            float toAlignmentForGestureState = isRecentsAnimationRunning
+                    && goingToUnstashedLauncherState() ? 1 : 0;
+            // If we're already animating to the value, just leave it be instead of restarting it.
+            if (!mIconAlignmentForGestureState.isAnimatingToValue(toAlignmentForGestureState)) {
+                Animator gestureAlignAnim = mIconAlignmentForGestureState
+                        .animateToValue(toAlignmentForGestureState);
+                if (isRecentsAnimationRunning) {
+                    gestureAlignAnim.setDuration(duration);
+                }
+                gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mIsAnimatingToLauncherViaGesture = false;
+                    }
+
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
+                    }
+                });
+                animatorSet.play(gestureAlignAnim);
+            }
+        }
+
+        if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
+            boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
+            animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
+                    .setDuration(duration));
+        }
 
         if (start) {
             animatorSet.start();
@@ -310,8 +331,11 @@
             animatorSet.play(stashAnimator);
         }
 
-        animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
-                .setDuration(duration));
+        // If we're already animating to the value, just leave it be instead of restarting it.
+        if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) {
+            animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
+                    .setDuration(duration));
+        }
     }
 
     private boolean isResumed() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 3cdcdf7..bec717d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -110,6 +110,13 @@
                 } else {
                     // Config change might be handled without re-creating the taskbar
                     if (mTaskbarActivityContext != null) {
+                        DeviceProfile dp = mUserUnlocked
+                                ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
+                                : null;
+
+                        if (dp != null && dp.isTaskbarPresent) {
+                            mTaskbarActivityContext.updateDeviceProfile(dp.copy(mContext));
+                        }
                         mTaskbarActivityContext.onConfigurationChanged(configDiff);
                     }
                 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 1cf50f7..ed71562 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -21,6 +21,7 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Pair;
@@ -58,6 +59,12 @@
         Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
         ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
                 .getActivityLaunchOptions(hostView);
+        Object itemInfo = hostView.getTag();
+        IBinder launchCookie = null;
+        if (itemInfo instanceof ItemInfo) {
+            launchCookie = mLauncher.getLaunchCookie((ItemInfo) itemInfo);
+            activityOptions.options.setLaunchCookie(launchCookie);
+        }
         if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) {
             // In the event this pending intent eventually launches an activity, i.e. a trampoline,
             // use the Quickstep transition animation.
@@ -65,17 +72,14 @@
                 ActivityTaskManager.getService()
                         .registerRemoteAnimationForNextActivityStart(
                                 pendingIntent.getCreatorPackage(),
-                                activityOptions.options.getRemoteAnimationAdapter());
+                                activityOptions.options.getRemoteAnimationAdapter(),
+                                launchCookie);
             } catch (RemoteException e) {
                 // Do nothing.
             }
         }
         activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
-        Object itemInfo = hostView.getTag();
-        if (itemInfo instanceof ItemInfo) {
-            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
-        }
         options = Pair.create(options.first, activityOptions.options);
         if (pendingIntent.isActivity()) {
             logAppLaunch(itemInfo);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 9f1e47f..2f52c9d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -249,6 +249,8 @@
     private RunningWindowAnim[] mRunningWindowAnim;
     // Possible second animation running at the same time as mRunningWindowAnim
     private Animator mParallelRunningAnim;
+    // Current running divider animation
+    private ValueAnimator mDividerAnimator;
     private boolean mIsMotionPaused;
     private boolean mHasMotionEverBeenPaused;
 
@@ -831,8 +833,8 @@
         // Notify when the animation starts
         flushOnRecentsAnimationAndLauncherBound();
 
-        TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                false /*shown*/, true /*animate*/);
+        // Start hiding the divider
+        setDividerShown(false, false /* immediate */);
 
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
@@ -849,8 +851,7 @@
         mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
 
         if (mRecentsAnimationTargets != null) {
-            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                    true /*shown*/, true /*animate*/);
+            setDividerShown(true, false /* immediate */);
         }
 
         // Defer clearing the controller and the targets until after we've updated the state
@@ -1000,8 +1001,7 @@
                     mStateCallback.setState(STATE_RESUME_LAST_TASK);
                 }
                 if (mRecentsAnimationTargets != null) {
-                    TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                            true /*shown*/, false /*animate*/);
+                    setDividerShown(true, true /* immediate */);
                 }
                 break;
         }
@@ -1653,8 +1653,7 @@
         mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
 
         if (mRecentsAnimationTargets != null) {
-            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                    true /*shown*/, false /*animate*/);
+            setDividerShown(true, true /* immediate */);
         }
 
         // Leave the pending invisible flag, as it may be used by wallpaper open animation.
@@ -1920,8 +1919,7 @@
     @Override
     public void onRecentsAnimationFinished(RecentsAnimationController controller) {
         if (!controller.getFinishTargetIsLauncher()) {
-            TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                    true /*shown*/, true /*animate*/);
+            setDividerShown(true, false /* immediate */);
         }
         mRecentsAnimationController = null;
         mRecentsAnimationTargets = null;
@@ -2026,6 +2024,19 @@
         return scaleProgress;
     }
 
+    private void setDividerShown(boolean shown, boolean immediate) {
+        if (mDividerAnimator != null) {
+            mDividerAnimator.cancel();
+        }
+        mDividerAnimator = TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
+                mRecentsAnimationTargets.nonApps, shown, (dividerAnimator) -> {
+                    dividerAnimator.start();
+                    if (immediate) {
+                        dividerAnimator.end();
+                    }
+                });
+    }
+
     /**
      * Used for winscope tracing, see launcher_trace.proto
      * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 95c8710..6a7d066 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -42,6 +42,8 @@
 
     private final Runnable mUpdateCallback;
     private ObjectAnimator mValueAnimator;
+    // Only non-null when an animation is playing to this value.
+    private Float mEndValue;
 
     public float value;
 
@@ -68,9 +70,17 @@
         mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
         mValueAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
+            public void onAnimationStart(Animator animator) {
+                if (mValueAnimator == animator) {
+                    mEndValue = end;
+                }
+            }
+
+            @Override
             public void onAnimationEnd(Animator animator) {
                 if (mValueAnimator == animator) {
                     mValueAnimator = null;
+                    mEndValue = null;
                 }
             }
         });
@@ -103,4 +113,15 @@
     public ObjectAnimator getCurrentAnimation() {
         return mValueAnimator;
     }
+
+    public boolean isAnimating() {
+        return mValueAnimator != null;
+    }
+
+    /**
+     * Returns whether we are currently animating, and the animation's end value matches the given.
+     */
+    public boolean isAnimatingToValue(float endValue) {
+        return isAnimating() && mEndValue != null && mEndValue == endValue;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0b09323..c455dc7 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -99,6 +99,8 @@
 
     default void onMotionEvent(MotionEvent ev) { }
 
+    default void onHoverEvent(MotionEvent ev) { }
+
     default void onKeyEvent(KeyEvent ev) { }
 
     default void onInputEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 75e8dd1..17baa3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -38,6 +38,7 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -271,6 +272,14 @@
         scheduleNextTask(cmd);
     }
 
+    public void dump(PrintWriter pw) {
+        pw.println("OverviewCommandHelper:");
+        pw.println("  mPendingCommands=" + mPendingCommands.size());
+        if (!mPendingCommands.isEmpty()) {
+            pw.println("    pendingCommandType=" + mPendingCommands.get(0).type);
+        }
+    }
+
     private static class CommandInfo {
         public final long createTime = SystemClock.elapsedRealtime();
         public final int type;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 103f350..1dc4933 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -99,6 +99,7 @@
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
     private static final long HOME_APPEAR_DURATION = 250;
+    private static final long RECENTS_ANIMATION_TIMEOUT = 1000;
 
     private RecentsDragLayer mDragLayer;
     private ScrimView mScrimView;
@@ -115,6 +116,11 @@
     // Strong refs to runners which are cleared when the activity is destroyed
     private RemoteAnimationFactory mActivityLaunchAnimationRunner;
 
+    // For handling degenerate cases where starting an activity doesn't actually trigger the remote
+    // animation callback
+    private final Handler mHandler = new Handler();
+    private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
+
     /**
      * Init drag layer and overview panel views.
      */
@@ -219,6 +225,16 @@
         // TODO(b/137318995) This should go home, but doing so removes freeform windows
     }
 
+    /**
+     * Called if the remote animation callback from #getActivityLaunchOptions() hasn't called back
+     * in a reasonable time due to a conflict with the recents animation.
+     */
+    private void onAnimationStartTimeout() {
+        if (mActivityLaunchAnimationRunner != null) {
+            mActivityLaunchAnimationRunner.onAnimationCancelled();
+        }
+    }
+
     @Override
     public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
         if (!(v instanceof TaskView)) {
@@ -233,6 +249,7 @@
             public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
                     RemoteAnimationTargetCompat[] wallpaperTargets,
                     RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets, nonAppTargets);
                 anim.addListener(resetStateListener());
@@ -242,6 +259,7 @@
 
             @Override
             public void onAnimationCancelled() {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 onEndCallback.executeAllAndDestroy();
             }
         };
@@ -256,6 +274,7 @@
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
                 onEndCallback);
         activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        mHandler.postDelayed(mAnimationStartTimeoutRunnable, RECENTS_ANIMATION_TIMEOUT);
         return activityOptions;
     }
 
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 73d1424..99a02e1 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -554,15 +554,13 @@
 
     /**
      * @param ev An ACTION_DOWN motion event
-     * @param task Info for the currently running task
      * @return whether the given motion event can trigger the assistant over the current task.
      */
-    public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
+    public boolean canTriggerAssistantAction(MotionEvent ev) {
         return mAssistantAvailable
                 && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
                 && mRotationTouchHelper.touchInAssistantRegion(ev)
-                && !isLockToAppActive()
-                && !isGestureBlockedActivity(task);
+                && !isLockToAppActive();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 5d9a537..30e225a 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -85,6 +85,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
@@ -538,8 +539,16 @@
                 nonAppTargets, depthController, pa);
         if (launcherClosing) {
             // TODO(b/182592057): differentiate between "restore split" vs "launch fullscreen app"
-            TaskViewUtils.setSplitAuxiliarySurfacesShown(nonAppTargets,
-                    true /*shown*/, true /*animate*/, pa);
+            TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, true /*shown*/,
+                    (dividerAnimator) -> {
+                        // If split apps are launching, we want to delay showing the divider bar
+                        // until the very end once the apps are mostly in place. This is because we
+                        // aren't moving the divider leash in the relative position with the
+                        // launching apps.
+                        dividerAnimator.setStartDelay(pa.getDuration()
+                                - SPLIT_DIVIDER_ANIM_DURATION);
+                        pa.add(dividerAnimator);
+                    });
         }
 
         Animator childStateAnimation = null;
@@ -594,16 +603,17 @@
         anim.addListener(windowAnimEndListener);
     }
 
-    public static void setSplitAuxiliarySurfacesShown(RemoteAnimationTargetCompat[] nonApps,
-            boolean shown, boolean animate) {
-        setSplitAuxiliarySurfacesShown(nonApps, shown, animate,null);
-    }
-
-    private static void setSplitAuxiliarySurfacesShown(
-            @NonNull RemoteAnimationTargetCompat[] nonApps, boolean shown, boolean animate,
-            @Nullable PendingAnimation splitLaunchAnimation) {
+    /**
+     * Creates an animation to show/hide the auxiliary surfaces (aka. divider bar), only calling
+     * {@param animatorHandler} if there are valid surfaces to animate.
+     *
+     * @return the animator animating the surfaces
+     */
+    public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
+            RemoteAnimationTargetCompat[] nonApps, boolean shown,
+            Consumer<ValueAnimator> animatorHandler) {
         if (nonApps == null || nonApps.length == 0) {
-            return;
+            return null;
         }
 
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -618,20 +628,7 @@
             }
         }
         if (!hasSurfaceToAnimate) {
-            return;
-        }
-
-        if (!animate) {
-            for (SurfaceControl leash : auxiliarySurfaces) {
-                t.setAlpha(leash, shown ? 1 : 0);
-                if (shown) {
-                    t.show(leash);
-                } else {
-                    t.hide(leash);
-                }
-            }
-            t.apply();
-            return;
+            return null;
         }
 
         ValueAnimator dockFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
@@ -668,15 +665,7 @@
             }
         });
         dockFadeAnimator.setDuration(SPLIT_DIVIDER_ANIM_DURATION);
-        if (splitLaunchAnimation != null) {
-            // If split apps are launching, we want to delay showing the divider bar until the very
-            // end once the apps are mostly in place. This is because we aren't moving the divider
-            // leash in the relative position with the launching apps.
-            dockFadeAnimator.setStartDelay(
-                    splitLaunchAnimation.getDuration() - SPLIT_DIVIDER_ANIM_DURATION);
-            splitLaunchAnimation.add(dockFadeAnimator);
-        } else {
-            dockFadeAnimator.start();
-        }
+        animatorHandler.accept(dockFadeAnimator);
+        return dockFadeAnimator;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 539239d..af8fec2 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -37,7 +37,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.TargetApi;
-import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.Service;
@@ -600,26 +599,14 @@
 
                 ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
                 mUncheckedConsumer = mConsumer;
-            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()) {
+            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+                    && mDeviceState.canTriggerAssistantAction(event)) {
                 mGestureState = createGestureState(mGestureState);
-                ActivityManager.RunningTaskInfo runningTask = mGestureState.getRunningTask();
-                if (mDeviceState.canTriggerAssistantAction(event, runningTask)) {
-                    // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
-                    // should not interrupt it. QuickSwitch assumes that interruption can only
-                    // happen if the next gesture is also quick switch.
-                    mUncheckedConsumer = new AssistantInputConsumer(
-                            this,
-                            mGestureState,
-                            InputConsumer.NO_OP, mInputMonitorCompat,
-                            mDeviceState,
-                            event);
-                } else if (mDeviceState.canTriggerOneHandedAction(event)) {
-                    // Consume gesture event for triggering one handed feature.
-                    mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
-                        InputConsumer.NO_OP, mInputMonitorCompat);
-                } else {
-                    mUncheckedConsumer = InputConsumer.NO_OP;
-                }
+                // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
+                // should not interrupt it. QuickSwitch assumes that interruption can only
+                // happen if the next gesture is also quick switch.
+                mUncheckedConsumer = tryCreateAssistantInputConsumer(
+                        InputConsumer.NO_OP, mGestureState, event);
             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
                 // Consume gesture event for triggering one handed feature.
                 mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
@@ -666,6 +653,14 @@
         ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
     }
 
+    private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base,
+            GestureState gestureState, MotionEvent motionEvent) {
+        return mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())
+                ? base
+                : new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat,
+                        mDeviceState, motionEvent);
+    }
+
     public GestureState createGestureState(GestureState previousGestureState) {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -711,9 +706,8 @@
             handleOrientationSetup(base);
         }
         if (mDeviceState.isFullyGesturalNavMode()) {
-            if (mDeviceState.canTriggerAssistantAction(event, newGestureState.getRunningTask())) {
-                base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat,
-                        mDeviceState, event);
+            if (mDeviceState.canTriggerAssistantAction(event)) {
+                base = tryCreateAssistantInputConsumer(base, newGestureState, event);
             }
 
             // If Taskbar is present, we listen for long press to unstash it.
@@ -967,6 +961,9 @@
             if (mOverviewComponentObserver != null) {
                 mOverviewComponentObserver.dump(pw);
             }
+            if (mOverviewCommandHelper != null) {
+                mOverviewCommandHelper.dump(pw);
+            }
             if (mGestureState != null) {
                 mGestureState.dump(pw);
             }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index b0df286..02ac48e 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -101,6 +101,13 @@
     }
 
     @Override
+    public void onHoverEvent(MotionEvent ev) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivity.dispatchGenericMotionEvent(ev);
+        }
+    }
+
+    @Override
     public void onKeyEvent(KeyEvent ev) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             switch (ev.getKeyCode()) {
diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index c2101a8..91b53c7 100644
--- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -71,7 +71,16 @@
 
     private boolean onInputConsumerEvent(InputEvent ev) {
         if (ev instanceof MotionEvent) {
-            onInputConsumerMotionEvent((MotionEvent) ev);
+            MotionEvent event = (MotionEvent) ev;
+            int action = event.getActionMasked();
+            boolean isHoverEvent = action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
+            if (isHoverEvent) {
+                onInputConsumerHoverEvent(event);
+            } else {
+                onInputConsumerMotionEvent(event);
+            }
         } else if (ev instanceof KeyEvent) {
             initInputConsumerIfNeeded();
             mInputConsumer.onKeyEvent((KeyEvent) ev);
@@ -113,6 +122,15 @@
         return true;
     }
 
+    private void onInputConsumerHoverEvent(MotionEvent ev) {
+        initInputConsumerIfNeeded();
+        if (mInputConsumer != null) {
+            SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
+                    mRotationSupplier.get());
+            mInputConsumer.onHoverEvent(ev);
+        }
+    }
+
     public void destroy() {
         if (mTouchInProgress) {
             mDestroyPending = true;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 02261af..7218cd7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -984,13 +984,17 @@
      */
     public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
         int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
-        if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
-            TransformParams params = mRemoteTargetHandles[0].getTransformParams();
-            RemoteAnimationTargets targets = params.getTargetSet();
-            if (targets != null && targets.findTask(taskId) != null) {
-                launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
-                        targets.nonApps);
-            }
+        if (mRunningTaskViewId == -1 ||
+                mRunningTaskViewId != runningTaskViewId ||
+                mRemoteTargetHandles == null) {
+            return;
+        }
+
+        TransformParams params = mRemoteTargetHandles[0].getTransformParams();
+        RemoteAnimationTargets targets = params.getTargetSet();
+        if (targets != null && targets.findTask(taskId) != null) {
+            launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
+                    targets.nonApps);
         }
     }
 
@@ -4139,8 +4143,10 @@
             anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
                     mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
             int runningTaskIndex = recentsView.getRunningTaskIndex();
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
-                    && runningTaskIndex != taskIndex) {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                    && runningTaskIndex != -1
+                    && runningTaskIndex != taskIndex
+                    && recentsView.getRemoteTargetHandles() != null) {
                 for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
                     anim.play(ObjectAnimator.ofFloat(
                             remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
@@ -4235,9 +4241,12 @@
             if (isSuccess) {
                 if (tv.getTaskIds()[1] != -1) {
                     // TODO(b/194414938): make this part of the animations instead.
-                    TaskViewUtils.setSplitAuxiliarySurfacesShown(mRemoteTargetHandles[0]
-                            .getTransformParams().getTargetSet().nonApps,
-                            true /*shown*/, false /*animate*/);
+                    TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
+                            mRemoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
+                            true /*shown*/, (dividerAnimator) -> {
+                                dividerAnimator.start();
+                                dividerAnimator.end();
+                            });
                 }
                 if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) {
                     finishRecentsAnimation(false /* toRecents */, null);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 6a2c997e..d046fef 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -462,11 +462,15 @@
         return getItemInfo(mTask);
     }
 
-    protected WorkspaceItemInfo getItemInfo(Task task) {
-        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+    protected WorkspaceItemInfo getItemInfo(@Nullable Task task) {
         WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
         stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
         stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+        if (task == null) {
+            return stubInfo;
+        }
+
+        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
         stubInfo.user = componentKey.user;
         stubInfo.intent = new Intent().setComponent(componentKey.componentName);
         stubInfo.title = task.title;
diff --git a/res/color-night-v31/accent_ripple_color.xml b/res/color-night-v31/accent_ripple_color.xml
new file mode 100644
index 0000000..cb149d6
--- /dev/null
+++ b/res/color-night-v31/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent1_300"/>
+</selector>
\ No newline at end of file
diff --git a/res/color-night/accent_ripple_color.xml b/res/color-night/accent_ripple_color.xml
new file mode 100644
index 0000000..4a37b00
--- /dev/null
+++ b/res/color-night/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#53BCAC"/>
+</selector>
\ No newline at end of file
diff --git a/res/color-v31/accent_ripple_color.xml b/res/color-v31/accent_ripple_color.xml
new file mode 100644
index 0000000..a996228
--- /dev/null
+++ b/res/color-v31/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent2_50"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/accent_ripple_color.xml b/res/color/accent_ripple_color.xml
new file mode 100644
index 0000000..697f415
--- /dev/null
+++ b/res/color/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#CDFAF1"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
index aea2e7a..8471cd4 100644
--- a/res/drawable/all_apps_tabs_background.xml
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -13,23 +13,36 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:enterFadeDuration="100">
-    <item
-        android:id="@+id/unselected"
-        android:state_selected="false">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/accent_ripple_color">
+
+    <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-            <solid android:color="@color/all_apps_tabs_background" />
+            <solid android:color="@color/accent_ripple_color" />
         </shape>
     </item>
 
-    <item
-        android:id="@+id/selected"
-        android:state_selected="true">
-        <shape android:shape="rectangle">
-            <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-            <solid android:color="@color/all_apps_tab_background_selected" />
-        </shape>
+    <item>
+        <selector android:enterFadeDuration="100">
+            <item
+                android:id="@+id/unselected"
+                android:state_selected="false">
+                <shape android:shape="rectangle">
+                    <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+                    <solid android:color="@color/all_apps_tabs_background" />
+                </shape>
+            </item>
+
+            <item
+                android:id="@+id/selected"
+                android:state_selected="true">
+                <shape android:shape="rectangle">
+                    <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+                    <solid android:color="@color/all_apps_tab_background_selected" />
+                </shape>
+            </item>
+        </selector>
     </item>
-</selector>
\ No newline at end of file
+
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/personal_work_tabs_ripple.xml b/res/drawable/personal_work_tabs_ripple.xml
deleted file mode 100644
index 2e57b80..0000000
--- a/res/drawable/personal_work_tabs_ripple.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <shape android:shape="rectangle">
-        <solid android:color="@android:color/transparent" />
-        <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-    </shape>
-</ripple>
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1f1d57a..163b442 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -35,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.icu.text.MessageFormat;
 import android.text.TextPaint;
+import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -785,7 +786,7 @@
                     invalidate();
                 }
             }
-            if (itemInfo.contentDescription != null) {
+            if (!TextUtils.isEmpty(itemInfo.contentDescription)) {
                 if (itemInfo.isDisabled()) {
                     setContentDescription(getContext().getString(R.string.disabled_app_label,
                             itemInfo.contentDescription));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d2fe483..63313f7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -460,9 +460,10 @@
      * Trims the string, removing all whitespace at the beginning and end of the string.
      * Non-breaking whitespaces are also removed.
      */
+    @NonNull
     public static String trim(CharSequence s) {
         if (s == null) {
-            return null;
+            return "";
         }
 
         // Just strip any sequence of whitespace or java space characters from the beginning and end
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 281dfea..c8dd003 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1686,7 +1686,7 @@
             PopupContainerWithArrow<Launcher> popupContainer = PopupContainerWithArrow
                     .showForIcon((BubbleTextView) child);
             if (popupContainer != null) {
-                dragOptions.preDragCondition = popupContainer.createPreDragCondition();
+                dragOptions.preDragCondition = popupContainer.createPreDragCondition(true);
             }
         }
 
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 08b38e8..178fbdb 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -202,8 +202,7 @@
      * Returns the title or empty string
      */
     private String getTitle() {
-        String title = getString(titleIndex);
-        return TextUtils.isEmpty(title) ? "" : Utilities.trim(title);
+        return Utilities.trim(getString(titleIndex));
     }
 
     /**
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b963950..6d2b12f 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -396,7 +396,7 @@
      * Current behavior:
      * - Start the drag if the touch passes a certain distance from the original touch down.
      */
-    public DragOptions.PreDragCondition createPreDragCondition() {
+    public DragOptions.PreDragCondition createPreDragCondition(boolean updateIconUi) {
         return new DragOptions.PreDragCondition() {
 
             @Override
@@ -406,6 +406,9 @@
 
             @Override
             public void onPreDragStart(DropTarget.DragObject dragObject) {
+                if (!updateIconUi) {
+                    return;
+                }
                 if (mIsAboveIcon) {
                     // Hide only the icon, keep the text visible.
                     mOriginalIcon.setIconVisible(false);
@@ -418,6 +421,9 @@
 
             @Override
             public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+                if (!updateIconUi) {
+                    return;
+                }
                 mOriginalIcon.setIconVisible(true);
                 if (dragStarted) {
                     // Make sure we keep the original icon hidden while it is being dragged.
@@ -480,6 +486,9 @@
     @Override
     protected void closeComplete() {
         super.closeComplete();
+        if (mActivityContext.getDragController() != null) {
+            mActivityContext.getDragController().removeDragListener(this);
+        }
         PopupContainerWithArrow openPopup = getOpen(mActivityContext);
         if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
             mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index dc3ee43..0a800c3 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -564,6 +564,12 @@
                 launcher, parent);
         view.recycle();
 
+        // Init properties before getting the drawable.
+        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
+        view.mIsOpening = isOpening;
+        view.mOriginalIcon = originalView;
+        view.mPositionOut = positionOut;
+
         // Get the drawable on the background thread
         boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
         if (shouldLoadIcon) {
@@ -577,11 +583,6 @@
         }
         sIconLoadResult = null;
 
-        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
-        view.mIsOpening = isOpening;
-        view.mOriginalIcon = originalView;
-        view.mPositionOut = positionOut;
-
         // Match the position of the original view.
         view.matchPositionOf(launcher, originalView, isOpening, positionOut);
 
@@ -639,6 +640,7 @@
         mLoadIconSignal = null;
         mEndRunnable = null;
         mFinalDrawableBounds.setEmpty();
+        mIsOpening = false;
         mPositionOut = null;
         mListenerView.setListener(null);
         mOriginalIcon = null;