Snap for 9635238 from 73a2334f7f33f8a5cf2b363af3aa35b4dd6b953c to tm-qpr3-release

Change-Id: I63f1cc237a7b640f65bdaea98a6233d049308ab3
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 6d778ef..ff7c138 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
-import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
 
 import android.animation.Animator;
 
@@ -93,7 +92,8 @@
     }
 
     private void animateToRecentsState(RecentsState toState) {
-        Animator anim = createAnimToRecentsState(toState, TASKBAR_STASH_DURATION);
+        Animator anim = createAnimToRecentsState(toState,
+                mControllers.taskbarStashController.getStashDuration());
         if (anim != null) {
             anim.start();
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 94d62b2..745defc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -425,7 +425,7 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        return (mViewCallbacks != null && mViewCallbacks.onKeyUp(keyCode, event))
+        return (mViewCallbacks != null && mViewCallbacks.onKeyUp(keyCode, event, mIsRtl))
                 || super.onKeyUp(keyCode, event);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index f0f361e..c1f764f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -19,6 +19,7 @@
 
 import android.animation.Animator;
 import android.view.KeyEvent;
+import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -125,18 +126,26 @@
     }
 
     private int launchTaskAt(int index) {
-        KeyboardQuickSwitchTaskView taskView = mKeyboardQuickSwitchView.getTaskAt(index);
+        if (mCloseAnimation != null) {
+            // Ignore taps on task views and alt key unpresses while the close animation is running.
+            return -1;
+        }
+        // Even with a valid index, this can be null if the user tries to quick switch before the
+        // views have been added in the KeyboardQuickSwitchView.
+        View taskView = mKeyboardQuickSwitchView.getTaskAt(index);
         GroupTask task = mControllerCallbacks.getTaskAt(index);
-        if (taskView == null || task == null) {
+        if (task == null) {
             return Math.max(0, index);
         } else if (task.task2 == null) {
             UI_HELPER_EXECUTOR.execute(() ->
                     ActivityManagerWrapper.getInstance().startActivityFromRecents(
                             task.task1.key,
                             mControllers.taskbarActivityContext.getActivityLaunchOptions(
-                                    taskView, null).options));
+                                    taskView == null ? mKeyboardQuickSwitchView : taskView, null)
+                                    .options));
         } else {
-            mControllers.uiController.launchSplitTasks(taskView, task);
+            mControllers.uiController.launchSplitTasks(
+                    taskView == null ? mKeyboardQuickSwitchView : taskView, task);
         }
         return -1;
     }
@@ -160,15 +169,26 @@
 
     class ViewCallbacks {
 
-        boolean onKeyUp(int keyCode, KeyEvent event) {
-            if (keyCode != KeyEvent.KEYCODE_TAB) {
+        boolean onKeyUp(int keyCode, KeyEvent event, boolean isRTL) {
+            if (keyCode != KeyEvent.KEYCODE_TAB
+                    && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT
+                    && keyCode != KeyEvent.KEYCODE_DPAD_LEFT
+                    && keyCode != KeyEvent.KEYCODE_GRAVE
+                    && keyCode != KeyEvent.KEYCODE_ESCAPE) {
                 return false;
             }
+            if (keyCode == KeyEvent.KEYCODE_GRAVE || keyCode == KeyEvent.KEYCODE_ESCAPE) {
+                closeQuickSwitchView(true);
+                return true;
+            }
+            boolean traverseBackwards = (keyCode == KeyEvent.KEYCODE_TAB && event.isShiftPressed())
+                    || (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && !isRTL)
+                    || (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && isRTL);
             int taskCount = mControllerCallbacks.getTaskCount();
             int toIndex = mCurrentFocusIndex == -1
                     // Focus the second-most recent app if possible
                     ? (taskCount > 1 ? 1 : 0)
-                    : (event.isShiftPressed()
+                    : (traverseBackwards
                             // focus a more recent task or loop back to the opposite end
                             ? Math.max(0, mCurrentFocusIndex == 0
                                     ? taskCount - 1 : mCurrentFocusIndex - 1)
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ac584bf..3046076 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -388,12 +388,16 @@
     }
 
     @Override
-    public void launchSplitTasks(View taskView, GroupTask groupTask) {
-        super.launchSplitTasks(taskView, groupTask);
+    public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
         mLauncher.launchSplitTasks(taskView, groupTask);
     }
 
     @Override
+    protected void onIconLayoutBoundsChanged() {
+        mTaskbarLauncherStateController.resetIconAlignment();
+    }
+
+    @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         super.dumpLogs(prefix, pw);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 0f25ba1..f082fc6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -38,7 +38,6 @@
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
-import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
 
@@ -152,6 +151,14 @@
         }
     }
 
+    /**
+     * Returns the stashed handle bounds.
+     * @param out The destination rect.
+     */
+    public void getStashedHandleBounds(Rect out) {
+        out.set(mStashedHandleBounds);
+    }
+
     private void initRegionSampler() {
         mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
                 new RegionSamplingHelper.SamplingCallback() {
@@ -194,16 +201,19 @@
      */
     public Animator createRevealAnimToIsStashed(boolean isStashed) {
         Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
+        float startRadius = mStashedHandleRadius;
 
         if (DisplayController.isTransientTaskbar(mActivity)) {
             // Account for the full visual height of the transient taskbar.
             int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
             visualBounds.top -= heightDiff;
             visualBounds.bottom += heightDiff;
+
+            startRadius = visualBounds.height() / 2f;
         }
 
         final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
-                mStashedHandleRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
+                startRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
 
         boolean isReversed = !isStashed;
         boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9b0f8c4..49dec1e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -560,8 +560,6 @@
     public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
         mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
                 fromInit);
-        mControllers.taskbarViewController.setImeIsVisible(
-                mControllers.navbarButtonsViewController.isImeVisible());
         int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 82f27ae..c03c916 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -21,6 +21,7 @@
 import android.graphics.Paint
 import android.graphics.Path
 import com.android.launcher3.R
+import com.android.launcher3.Utilities.mapRange
 import com.android.launcher3.Utilities.mapToRange
 import com.android.launcher3.anim.Interpolators
 import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
@@ -51,6 +52,12 @@
     private val invertedLeftCornerPath: Path = Path()
     private val invertedRightCornerPath: Path = Path()
 
+    private val stashedHandleWidth =
+        context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width)
+
+    private val stashedHandleHeight =
+        context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_height)
+
     init {
         paint.color = context.getColor(R.color.taskbar_background)
         paint.flags = Paint.ANTI_ALIAS_FLAG
@@ -98,8 +105,8 @@
     /** Draws the background with the given paint and height, on the provided canvas. */
     fun draw(canvas: Canvas) {
         canvas.save()
-        canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
         if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
+            canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
             // Draw the background behind taskbar content.
             canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
 
@@ -110,13 +117,21 @@
             canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
             canvas.drawPath(invertedRightCornerPath, paint)
         } else {
-            // Approximates the stash/unstash animation to transform the background.
-            val scaleFactor = backgroundHeight / maxBackgroundHeight
-            val width = transientBackgroundBounds.width()
-            val widthScale = mapToRange(scaleFactor, 0f, 1f, 0.2f, 1f, Interpolators.LINEAR)
-            val newWidth = widthScale * width
-            val delta = width - newWidth
-            canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f))
+            // backgroundHeight is a value from [0...maxBackgroundHeight], so we can use it as a
+            // proxy to figure out the animation progress of the stash/unstash animation.
+            val progress = backgroundHeight / maxBackgroundHeight
+
+            // At progress 0, we draw the background as the stashed handle.
+            // At progress 1, we draw the background as the full taskbar.
+            val newBackgroundHeight =
+                mapRange(progress, stashedHandleHeight.toFloat(), maxBackgroundHeight)
+            val fullWidth = transientBackgroundBounds.width()
+            val newWidth = mapRange(progress, stashedHandleWidth.toFloat(), fullWidth.toFloat())
+            val halfWidthDelta = (fullWidth - newWidth) / 2f
+            val radius = newBackgroundHeight / 2f
+            val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
+
+            canvas.translate(0f, canvas.height - bottomMargin + bottomMarginProgress)
 
             // Draw shadow.
             val shadowAlpha =
@@ -128,20 +143,20 @@
                 setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
             )
 
-            // Draw background.
-            val radius = backgroundHeight / 2f
+            // Aligns the bottom with the bottom of the stashed handle.
+            val bottom =
+                (-mapRange(1f - progress, 0f, stashedHandleHeight / 2f) + translationYForSwipe)
 
             canvas.drawRoundRect(
-                transientBackgroundBounds.left + (delta / 2f),
-                translationYForSwipe,
-                transientBackgroundBounds.right - (delta / 2f),
-                backgroundHeight + translationYForSwipe,
+                transientBackgroundBounds.left + halfWidthDelta,
+                bottom - newBackgroundHeight,
+                transientBackgroundBounds.right - halfWidthDelta,
+                bottom,
                 radius,
                 radius,
                 paint
             )
         }
-
         canvas.restore()
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 6432119..1c6aca7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
-import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED;
 
@@ -41,6 +40,7 @@
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.launcher3.util.window.RefreshRateTracker;
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.views.RecentsView;
@@ -139,8 +139,7 @@
         mIconAlphaForHome = mControllers.taskbarViewController
                 .getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
 
-        mIconAlignment.finishAnimation();
-        onIconAlignmentRatioChanged();
+        resetIconAlignment();
 
         mLauncher.getStateManager().addStateListener(mStateListener);
 
@@ -234,7 +233,7 @@
     }
 
     public void applyState() {
-        applyState(TASKBAR_STASH_DURATION);
+        applyState(mControllers.taskbarStashController.getStashDuration());
     }
 
     public void applyState(long duration) {
@@ -242,7 +241,7 @@
     }
 
     public Animator applyState(boolean start) {
-        return applyState(TASKBAR_STASH_DURATION, start);
+        return applyState(mControllers.taskbarStashController.getStashDuration(), start);
     }
 
     public Animator applyState(long duration, boolean start) {
@@ -329,8 +328,17 @@
                         + mTaskbarBackgroundAlpha.value
                         + " -> " + backgroundAlpha + ": " + duration);
             }
-            animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(backgroundAlpha)
-                    .setDuration(duration));
+
+            Animator taskbarBackgroundAlpha = mTaskbarBackgroundAlpha
+                    .animateToValue(backgroundAlpha)
+                    .setDuration(duration);
+            // Add a single frame delay to the taskbar bg to avoid too many moving parts during the
+            // app launch animation.
+            taskbarBackgroundAlpha.setStartDelay(
+                    (hasAnyFlag(changedFlags, FLAG_RESUMED) && !goingToLauncher)
+                            ? RefreshRateTracker.getSingleFrameMs(mLauncher)
+                            : 0);
+            animatorSet.play(taskbarBackgroundAlpha);
         }
 
         float cornerRoundness = goingToLauncher ? 0 : 1;
@@ -433,6 +441,14 @@
         return (mState & FLAGS_LAUNCHER) != 0;
     }
 
+    /**
+     * Resets and updates the icon alignment.
+     */
+    protected void resetIconAlignment() {
+        mIconAlignment.finishAnimation();
+        onIconAlignmentRatioChanged();
+    }
+
     private void onIconAlignmentRatioChanged() {
         float currentValue = mIconAlphaForHome.getValue();
         boolean taskbarWillBeVisible = mIconAlignment.value < 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 2f69b07..50f5d9e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,8 +17,10 @@
 
 import static android.view.HapticFeedbackConstants.LONG_PRESS;
 
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
@@ -41,6 +43,7 @@
 import android.view.InsetsController;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -101,11 +104,20 @@
 
     /**
      * How long to stash/unstash when manually invoked via long press.
+     *
+     * Use {@link #getStashDuration()} to query duration
      */
-    public static final long TASKBAR_STASH_DURATION =
+    private static final long TASKBAR_STASH_DURATION =
             InsetsController.ANIMATION_DURATION_RESIZE;
 
     /**
+     * How long to stash/unstash transient taskbar.
+     *
+     * Use {@link #getStashDuration()} to query duration.
+     */
+    private static final long TRANSIENT_TASKBAR_STASH_DURATION = 417;
+
+    /**
      * How long to stash/unstash when keyboard is appearing/disappearing.
      */
     private static final long TASKBAR_STASH_DURATION_FOR_IME = 80;
@@ -113,7 +125,7 @@
     /**
      * The scale TaskbarView animates to when being stashed.
      */
-    private static final float STASHED_TASKBAR_SCALE = 0.3f;
+    protected static final float STASHED_TASKBAR_SCALE = 0.5f;
 
     /**
      * How long the hint animation plays, starting on motion down.
@@ -122,6 +134,21 @@
             ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
 
     /**
+     * How long to delay the icon/stash handle alpha.
+     */
+    private static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
+
+    /**
+     * How long the icon/stash handle alpha animation plays.
+     */
+    private static final long TASKBAR_STASH_ALPHA_DURATION = 50;
+
+    /**
+     * How long to delay the icon/stash handle alpha for the home to app taskbar animation.
+     */
+    private static final long TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY = 66;
+
+    /**
      * The scale that TaskbarView animates to when hinting towards the stashed state.
      */
     private static final float STASHED_TASKBAR_HINT_SCALE = 0.9f;
@@ -299,7 +326,16 @@
         boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete();
         updateStateForFlag(FLAG_IN_SETUP, hideTaskbar);
         updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar);
-        applyState(hideTaskbar ? 0 : TASKBAR_STASH_DURATION);
+        applyState(hideTaskbar ? 0 : getStashDuration());
+    }
+
+    /**
+     * Returns how long the stash/unstash animation should play.
+     */
+    public long getStashDuration() {
+        return DisplayController.isTransientTaskbar(mActivity)
+                ? TRANSIENT_TASKBAR_STASH_DURATION
+                : TASKBAR_STASH_DURATION;
     }
 
     /**
@@ -514,7 +550,10 @@
         }
         mAnimator = new AnimatorSet();
         addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
-        final float stashTranslation = isPhoneMode() ? 0 : (mUnstashedHeight - mStashedHeight);
+        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+        final float stashTranslation = isPhoneMode() || isTransientTaskbar
+                ? 0
+                : (mUnstashedHeight - mStashedHeight);
 
         if (!supportsVisualStashing()) {
             // Just hide/show the icons and background instead of stashing into a handle.
@@ -522,8 +561,8 @@
                     .setDuration(duration));
             mAnimator.playTogether(mTaskbarBackgroundOffset.animateToValue(isStashed ? 1 : 0)
                     .setDuration(duration));
-            mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed ?
-                            stashTranslation : 0)
+            mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed
+                            ? stashTranslation : 0)
                     .setDuration(duration));
             mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
                     hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
@@ -531,6 +570,40 @@
             return;
         }
 
+        // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
+        // already stashed Taskbar.
+        boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
+                && hasAnyFlag(changedFlags, FLAG_IN_APP);
+        if (isTransientTaskbar) {
+            createTransientAnimToIsStashed(mAnimator, isStashed, duration, animateBg, changedFlags,
+                    skipStashAnimation);
+        } else {
+            createAnimToIsStashed(mAnimator, isStashed, duration, animateBg, skipStashAnimation,
+                    stashTranslation);
+        }
+
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mIsStashed = isStashed;
+                onIsStashedChanged(mIsStashed);
+
+                cancelTimeoutIfExists();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimator = null;
+
+                if (!mIsStashed) {
+                    tryStartTaskbarTimeout();
+                }
+            }
+        });
+    }
+
+    private void createAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
+            boolean animateBg, boolean skipStashAnimation, float stashTranslation) {
         AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
         // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
         AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
@@ -539,10 +612,6 @@
         final float firstHalfDurationScale;
         final float secondHalfDurationScale;
 
-        // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
-        // already stashed Taskbar.
-        boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
-                && hasAnyFlag(changedFlags, FLAG_IN_APP);
         if (isStashed) {
             firstHalfDurationScale = 0.75f;
             secondHalfDurationScale = 0.5f;
@@ -595,10 +664,6 @@
             }
         }
 
-        if (DisplayController.isTransientTaskbar(mActivity)) {
-            fullLengthAnimatorSet.play(mControllers.taskbarViewController
-                    .createRevealAnimToIsStashed(isStashed));
-        }
         fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
                 .createRevealAnimToIsStashed(isStashed));
         // Return the stashed handle to its default scale in case it was changed as part of the
@@ -610,26 +675,73 @@
         secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
         secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
 
-        mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
+        as.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
                 secondHalfAnimatorSet);
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mIsStashed = isStashed;
-                onIsStashedChanged(mIsStashed);
 
-                cancelTimeoutIfExists();
+    }
+
+    private void createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
+            boolean animateBg, int changedFlags, boolean skipStashAnimation) {
+        Interpolator skipInterpolator = null;
+
+        if (isStashed) {
+            if (animateBg) {
+                play(as, mTaskbarBackgroundOffset.animateToValue(1), 0, duration, EMPHASIZED);
+            } else {
+                as.addListener(AnimatorListeners.forEndCallback(
+                        () -> mTaskbarBackgroundOffset.updateValue(1)));
             }
 
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimator = null;
+            long alphaStartDelay = duration == 0 ? 0 : (changedFlags == FLAG_IN_APP)
+                    ? TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY
+                    : TASKBAR_STASH_ALPHA_START_DELAY;
+            long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
+            play(as, mIconAlphaForStash.animateToValue(0), alphaStartDelay, alphaDuration, LINEAR);
+            play(as, mTaskbarStashedHandleAlpha.animateToValue(1), alphaStartDelay,
+                    Math.max(0, duration - alphaStartDelay), LINEAR);
 
-                if (!mIsStashed) {
-                    tryStartTaskbarTimeout();
-                }
+            if (skipStashAnimation) {
+                skipInterpolator = INSTANT;
             }
-        });
+        } else  {
+            if (animateBg) {
+                play(as, mTaskbarBackgroundOffset.animateToValue(0), 0, duration, EMPHASIZED);
+            } else {
+                as.addListener(AnimatorListeners.forEndCallback(
+                        () -> mTaskbarBackgroundOffset.updateValue(0)));
+            }
+
+            long alphaStartDelay = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_START_DELAY;
+            long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
+            play(as, mIconAlphaForStash.animateToValue(1), alphaStartDelay, alphaDuration, LINEAR);
+            play(as, mTaskbarStashedHandleAlpha.animateToValue(0), 0, alphaDuration, LINEAR);
+
+            if (skipStashAnimation) {
+                skipInterpolator = FINAL_FRAME;
+            }
+        }
+        play(as, mControllers.taskbarViewController
+                .createRevealAnimToIsStashed(isStashed, isInApp()), 0, duration, EMPHASIZED);
+
+        if (skipInterpolator != null) {
+            as.setInterpolator(skipInterpolator);
+        }
+
+        play(as, mControllers.stashedHandleViewController
+                .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
+
+        // Return the stashed handle to its default scale in case it was changed as part of the
+        // feedforward hint. Note that the reveal animation above also visually scales it.
+        as.play(mTaskbarStashedHandleHintScale.animateToValue(1f)
+                .setDuration(isStashed ? duration / 2 : duration));
+    }
+
+    private static void play(AnimatorSet as, Animator a, long startDelay, long duration,
+            Interpolator interpolator) {
+        a.setDuration(duration);
+        a.setStartDelay(startDelay);
+        a.setInterpolator(interpolator);
+        as.play(a);
     }
 
     private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
@@ -711,7 +823,6 @@
         }
     }
 
-
     /**
      * Returns an animator which applies the latest state if mIsStashed is changed, or {@code null}
      * otherwise.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 76b8b6d..24a089c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -25,6 +25,7 @@
 import android.view.View;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.model.data.ItemInfo;
@@ -75,6 +76,11 @@
 
     protected void onStashedInAppChanged() { }
 
+    /**
+     * Called when taskbar icon layout bounds change.
+     */
+    protected void onIconLayoutBoundsChanged() { }
+
     /** Called when an icon is launched. */
     @CallSuper
     public void onTaskbarIconLaunched(ItemInfo item) {
@@ -248,15 +254,6 @@
     }
 
     /**
-     * Closes the Keyboard Quick Switch View.
-     *
-     * No-op if the view is already closed
-     */
-    public void closeQuickSwitchView() {
-        mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
-    }
-
-    /**
      * Launches the focused task and closes the Keyboard Quick Switch View.
      *
      * If the overlay or view are closed, or the overview task is focused, then Overview is
@@ -275,5 +272,5 @@
      *
      * No-op if the view is not yet open.
      */
-    public void launchSplitTasks(View taskview, GroupTask groupTask) { }
+    public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index eddc278..c1e85aa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -58,6 +58,8 @@
     private static final String TAG = TaskbarView.class.getSimpleName();
 
     private static final float TASKBAR_BACKGROUND_LUMINANCE = 0.30f;
+    private static final Rect sTmpRect = new Rect();
+
     public int mThemeIconsBackground;
 
     private final int[] mTempOutLocation = new int[2];
@@ -74,9 +76,6 @@
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
-    // Prevents dispatching touches to children if true
-    private boolean mTouchEnabled = true;
-
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
@@ -339,6 +338,9 @@
                     (right - navSpaceNeeded) - iconEnd;
             iconEnd += offset;
         }
+
+        sTmpRect.set(mIconLayoutBounds);
+
         // Layout the children
         mIconLayoutBounds.right = iconEnd;
         mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
@@ -379,14 +381,10 @@
             mIconLayoutBounds.right = center + distanceFromCenter;
             mIconLayoutBounds.left = center - distanceFromCenter;
         }
-    }
 
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (!mTouchEnabled) {
-            return true;
+        if (!sTmpRect.equals(mIconLayoutBounds)) {
+            mControllerCallbacks.notifyIconLayoutBoundsChanged();
         }
-        return super.dispatchTouchEvent(ev);
     }
 
     @Override
@@ -397,9 +395,6 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (!mTouchEnabled) {
-            return true;
-        }
         if (mIconLayoutBounds.left <= event.getX()
                 && event.getX() <= mIconLayoutBounds.right
                 && !DisplayController.isTransientTaskbar(mActivityContext)) {
@@ -420,11 +415,6 @@
         return super.onTouchEvent(event);
     }
 
-    public void setTouchesEnabled(boolean touchEnabled) {
-        this.mTouchEnabled = touchEnabled;
-        mControllerCallbacks.clearTouchInProgress();
-    }
-
     /**
      * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
      * touch bounds.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6252e60..c708838 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,9 +17,9 @@
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -28,6 +28,7 @@
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
 
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.graphics.Rect;
@@ -199,14 +200,6 @@
     }
 
     /**
-     * Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
-     */
-    public void setImeIsVisible(boolean isImeVisible) {
-        mTaskbarView.setTouchesEnabled(!isImeVisible
-                || DisplayController.isTransientTaskbar(mActivity));
-    }
-
-    /**
      * Should be called when the recents button is disabled, so we can hide taskbar icons as well.
      */
     public void setRecentsButtonDisabled(boolean isDisabled) {
@@ -310,13 +303,48 @@
      * @param isStashed When true, the icon crops vertically to the size of the stashed handle.
      *                  When false, the reverse happens.
      */
-    public AnimatorSet createRevealAnimToIsStashed(boolean isStashed) {
+    public AnimatorSet createRevealAnimToIsStashed(boolean isStashed, boolean isInApp) {
         AnimatorSet as = new AnimatorSet();
-        for (int i = mTaskbarView.getChildCount() - 1; i >= 0; i--) {
+
+        Rect stashedBounds = new Rect();
+        mControllers.stashedHandleViewController.getStashedHandleBounds(stashedBounds);
+
+        int numChildren = mTaskbarView.getChildCount();
+        // We do not actually modify the width of the icons, but we will use this width to position
+        // the children to overlay the nav handle.
+        float virtualChildWidth = stashedBounds.width() / (float) numChildren;
+
+        for (int i = numChildren - 1; i >= 0; i--) {
             View child = mTaskbarView.getChildAt(i);
-            if (child instanceof BubbleTextView) {
-                as.play(createRevealAnimForView(child, isStashed));
+
+            // Crop the icons to/from the nav handle shape.
+            as.play(createRevealAnimForView(child, isStashed));
+
+            // Translate the icons to/from their locations as the "nav handle."
+            // We look at 'left' and 'right' values to ensure that the children stay within the
+            // bounds of the stashed handle.
+            float iconLeft = child.getLeft();
+            float newLeft = stashedBounds.left + (virtualChildWidth * i);
+            final float croppedTransX;
+            if (iconLeft > newLeft) {
+                float newRight = stashedBounds.right - (virtualChildWidth * (numChildren - 1 - i));
+                croppedTransX = -(child.getLeft() + child.getWidth() - newRight);
+            } else {
+                croppedTransX = newLeft - iconLeft;
             }
+
+            as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_X, isStashed
+                    ? new float[] {croppedTransX}
+                    : new float[] {croppedTransX, 0}));
+
+            float croppedTransY = child.getHeight() - stashedBounds.height();
+            as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_Y, isStashed
+                    ? new float[] {croppedTransY}
+                    : new float[] {croppedTransY, 0}));
+            as.addListener(forEndCallback(() -> {
+                ICON_REVEAL_TRANSLATE_X.set(child, 0f);
+                ICON_REVEAL_TRANSLATE_Y.set(child, 0f);
+            }));
         }
         return as;
     }
@@ -409,7 +437,7 @@
                 float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
                 setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
 
-                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+                setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
                 if (mIsHotseatIconOnTopWhenAligned) {
                     setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
@@ -448,7 +476,7 @@
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
             setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
 
-            setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+            setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
             setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
         }
@@ -626,6 +654,13 @@
         public void clearTouchInProgress() {
             mTouchInProgress = false;
         }
+
+        /**
+         * Notifies launcher to update icon alignment.
+         */
+        public void notifyIconLayoutBoundsChanged() {
+            mControllers.uiController.onIconLayoutBoundsChanged();
+        }
     }
 
     public static final FloatProperty<View> ICON_TRANSLATE_X =
@@ -636,7 +671,7 @@
                     if (view instanceof BubbleTextView) {
                         ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
                     } else if (view instanceof FolderIcon) {
-                        ((FolderIcon) view).setTranslationForTaskbarAlignmentAnimation(v);
+                        ((FolderIcon) view).setTranslationXForTaskbarAlignmentAnimation(v);
                     } else {
                         view.setTranslationX(v);
                     }
@@ -653,4 +688,81 @@
                     return view.getTranslationX();
                 }
             };
+
+    public static final FloatProperty<View> ICON_TRANSLATE_Y =
+            new FloatProperty<View>("taskbarAlignmentTranslateY") {
+
+                @Override
+                public void setValue(View view, float v) {
+                    if (view instanceof BubbleTextView) {
+                        ((BubbleTextView) view).setTranslationYForTaskbarAlignmentAnimation(v);
+                    } else if (view instanceof FolderIcon) {
+                        ((FolderIcon) view).setTranslationYForTaskbarAlignmentAnimation(v);
+                    } else {
+                        view.setTranslationY(v);
+                    }
+                }
+
+                @Override
+                public Float get(View view) {
+                    if (view instanceof BubbleTextView) {
+                        return ((BubbleTextView) view)
+                                .getTranslationYForTaskbarAlignmentAnimation();
+                    } else if (view instanceof FolderIcon) {
+                        return ((FolderIcon) view).getTranslationYForTaskbarAlignmentAnimation();
+                    }
+                    return view.getTranslationY();
+                }
+            };
+
+    public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_X =
+            new FloatProperty<View>("taskbarRevealTranslateX") {
+
+                @Override
+                public void setValue(View view, float v) {
+                    if (view instanceof BubbleTextView) {
+                        ((BubbleTextView) view).setTranslationXForTaskbarRevealAnimation(v);
+                    } else if (view instanceof FolderIcon) {
+                        ((FolderIcon) view).setTranslationXForTaskbarRevealAnimation(v);
+                    } else {
+                        view.setTranslationX(v);
+                    }
+                }
+
+                @Override
+                public Float get(View view) {
+                    if (view instanceof BubbleTextView) {
+                        return ((BubbleTextView) view).getTranslationXForTaskbarRevealAnimation();
+                    } else if (view instanceof FolderIcon) {
+                        return ((FolderIcon) view).getTranslationXForTaskbarRevealAnimation();
+                    }
+                    return view.getTranslationX();
+                }
+            };
+
+    public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_Y =
+            new FloatProperty<View>("taskbarRevealTranslateY") {
+
+                @Override
+                public void setValue(View view, float v) {
+                    if (view instanceof BubbleTextView) {
+                        ((BubbleTextView) view).setTranslationYForTaskbarRevealAnimation(v);
+                    } else if (view instanceof FolderIcon) {
+                        ((FolderIcon) view).setTranslationYForTaskbarRevealAnimation(v);
+                    } else {
+                        view.setTranslationY(v);
+                    }
+                }
+
+                @Override
+                public Float get(View view) {
+                    if (view instanceof BubbleTextView) {
+                        return ((BubbleTextView) view).getTranslationYForTaskbarRevealAnimation();
+                    } else if (view instanceof FolderIcon) {
+                        return ((FolderIcon) view).getTranslationYForTaskbarRevealAnimation();
+                    }
+                    return view.getTranslationY();
+                }
+            };
+
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 80ce369..46cd045 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1214,7 +1214,7 @@
      *
      * If the second split task is missing, launches the first task normally.
      */
-    public void launchSplitTasks(View taskView, GroupTask groupTask) {
+    public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
         if (groupTask.task2 == null) {
             UI_HELPER_EXECUTOR.execute(() ->
                     ActivityManagerWrapper.getInstance().startActivityFromRecents(
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index edbce10..b109e8a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -104,6 +104,10 @@
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
 
     private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+    private float mTranslationYForTaskbarAlignmentAnimation = 0f;
+
+    private float mTranslationXForTaskbarRevealAnimation = 0f;
+    private float mTranslationYForTaskbarRevealAnimation = 0f;
 
     private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
 
@@ -952,11 +956,17 @@
     }
 
     private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+        super.setTranslationX(mTranslationForReorderBounce.x
+                + mTranslationForReorderPreview.x
                 + mTranslationForMoveFromCenterAnimation.x
-                + mTranslationXForTaskbarAlignmentAnimation);
-        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
-                + mTranslationForMoveFromCenterAnimation.y);
+                + mTranslationXForTaskbarAlignmentAnimation
+                + mTranslationXForTaskbarRevealAnimation
+        );
+        super.setTranslationY(mTranslationForReorderBounce.y
+                + mTranslationForReorderPreview.y
+                + mTranslationForMoveFromCenterAnimation.y
+                + mTranslationYForTaskbarAlignmentAnimation
+                + mTranslationYForTaskbarRevealAnimation);
     }
 
     public void setReorderBounceOffset(float x, float y) {
@@ -1012,6 +1022,51 @@
         return mTranslationXForTaskbarAlignmentAnimation;
     }
 
+    /**
+     * Sets translationX for taskbar to launcher alignment animation
+     */
+    public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
+        mTranslationYForTaskbarAlignmentAnimation = translationY;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translationY value for taskbar to launcher alignment animation
+     */
+    public float getTranslationYForTaskbarAlignmentAnimation() {
+        return mTranslationYForTaskbarAlignmentAnimation;
+    }
+
+    /**
+     * Sets translationX value for taskbar reveal animation
+     */
+    public void setTranslationXForTaskbarRevealAnimation(float translationX) {
+        mTranslationXForTaskbarRevealAnimation = translationX;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translation values for taskbar reveal animation
+     */
+    public float getTranslationXForTaskbarRevealAnimation() {
+        return mTranslationXForTaskbarRevealAnimation;
+    }
+
+    /**
+     * Sets translationY value for taskbar reveal animation
+     */
+    public void setTranslationYForTaskbarRevealAnimation(float translationY) {
+        mTranslationYForTaskbarRevealAnimation = translationY;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translationY values for taskbar reveal animation
+     */
+    public float getTranslationYForTaskbarRevealAnimation() {
+        return mTranslationYForTaskbarRevealAnimation;
+    }
+
     public View getView() {
         return this;
     }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index e69f781..ee1a060 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -135,6 +135,9 @@
 
     private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
     private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+    private float mTranslationYForTaskbarAlignmentAnimation = 0f;
+    private float mTranslationXForTaskbarRevealAnimation = 0f;
+    private float mTranslationYForTaskbarRevealAnimation = 0f;
 
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -416,7 +419,7 @@
                     () -> {
                         mPreviewItemManager.hidePreviewItem(finalIndex, false);
                         mFolder.showItem(item);
-                    }, 
+                    },
                     DragLayer.ANIMATION_END_DISAPPEAR, null);
 
             mFolder.hideItem(item);
@@ -768,11 +771,15 @@
     }
 
     private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+        super.setTranslationX(mTranslationForReorderBounce.x
+                + mTranslationForReorderPreview.x
                 + mTranslationForMoveFromCenterAnimation.x
-                + mTranslationXForTaskbarAlignmentAnimation);
+                + mTranslationXForTaskbarAlignmentAnimation
+                + mTranslationXForTaskbarRevealAnimation);
         super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
-                + mTranslationForMoveFromCenterAnimation.y);
+                + mTranslationForMoveFromCenterAnimation.y
+                + mTranslationYForTaskbarAlignmentAnimation
+                + mTranslationYForTaskbarRevealAnimation);
     }
 
     public void setReorderBounceOffset(float x, float y) {
@@ -787,7 +794,7 @@
     /**
      * Sets translationX value for taskbar to launcher alignment animation
      */
-    public void setTranslationForTaskbarAlignmentAnimation(float translationX) {
+    public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
         mTranslationXForTaskbarAlignmentAnimation = translationX;
         updateTranslation();
     }
@@ -800,6 +807,51 @@
     }
 
     /**
+     * Sets translationY value for taskbar to launcher alignment animation
+     */
+    public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
+        mTranslationYForTaskbarAlignmentAnimation = translationY;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translation values for taskbar to launcher alignment animation
+     */
+    public float getTranslationYForTaskbarAlignmentAnimation() {
+        return mTranslationYForTaskbarAlignmentAnimation;
+    }
+
+    /**
+     * Sets translationX value for taskbar reveal animation
+     */
+    public void setTranslationXForTaskbarRevealAnimation(float translationX) {
+        mTranslationXForTaskbarRevealAnimation = translationX;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translation values for taskbar reveal animation
+     */
+    public float getTranslationXForTaskbarRevealAnimation() {
+        return mTranslationXForTaskbarRevealAnimation;
+    }
+
+    /**
+     * Sets translationY value for taskbar reveal animation
+     */
+    public void setTranslationYForTaskbarRevealAnimation(float translationY) {
+        mTranslationYForTaskbarRevealAnimation = translationY;
+        updateTranslation();
+    }
+
+    /**
+     * Returns translationY values for taskbar reveal animation
+     */
+    public float getTranslationYForTaskbarRevealAnimation() {
+        return mTranslationYForTaskbarRevealAnimation;
+    }
+
+    /**
      * Sets translation values for move from center animation
      */
     public void setTranslationForMoveFromCenterAnimation(float x, float y) {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index f7837f5..b6f6223 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -313,7 +313,7 @@
      */
     default boolean startActivitySafely(
             View v, Intent intent, @Nullable ItemInfo item) {
-
+        Preconditions.assertUIThread();
         Context context = (Context) this;
         if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
             Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();