Merge "Additional polish for the NORMAL<->ALL_APPS transitions." into tm-dev
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 0c7eba3..7c3a160 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -33,6 +33,8 @@
             launcher:minHeightDps="491.33"
             launcher:iconImageSize="60"
             launcher:iconTextSize="14.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
     </grid-option>
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index a3e8b5c..ca30e72 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
 import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.annotation.ColorInt;
 import android.os.RemoteException;
 import android.util.Log;
@@ -28,6 +29,7 @@
 import android.view.WindowManagerGlobal;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BaseQuickstepLauncher;
@@ -140,6 +142,24 @@
     }
 
     /**
+     * Adds the Launcher resume animator to the given animator set.
+     *
+     * This should be used to run a Launcher resume animation whose progress matches a
+     * swipe progress.
+     *
+     * @param placeholderDuration a placeholder duration to be used to ensure all full-length
+     *                            sub-animations are properly coordinated. This duration should not
+     *                            actually be used since this animation tracks a swipe progress.
+     */
+    protected void addLauncherResumeAnimation(AnimatorSet animation, int placeholderDuration) {
+        animation.play(onLauncherResumedOrPaused(
+                /* isResumed= */ true,
+                /* fromInit= */ false,
+                /* startAnimation= */ false,
+                placeholderDuration));
+    }
+
+    /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
@@ -147,9 +167,19 @@
     }
 
     private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) {
+        onLauncherResumedOrPaused(
+                isResumed,
+                fromInit,
+                /* startAnimation= */ true,
+                QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
+    }
+
+    @Nullable
+    private Animator onLauncherResumedOrPaused(
+            boolean isResumed, boolean fromInit, boolean startAnimation, int duration) {
         if (mKeyguardController.isScreenOff()) {
             if (!isResumed) {
-                return;
+                return null;
             } else {
                 // Resuming implicitly means device unlocked
                 mKeyguardController.setScreenOn();
@@ -157,8 +187,7 @@
         }
 
         mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
-        mTaskbarLauncherStateController.applyState(
-                fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
+        return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 2f32219..ed1001c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,6 +29,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 
 import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -60,6 +61,8 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -735,6 +738,45 @@
     }
 
     /**
+     * Displays a single frame of the Launcher start from SUW animation.
+     *
+     * This animation is a combination of the Launcher resume animation, which animates the hotseat
+     * icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar
+     * stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps
+     * button.
+     *
+     * This should be used to run a Taskbar unstash to hotseat animation whose progress matches a
+     * swipe progress.
+     *
+     * @param duration a placeholder duration to be used to ensure all full-length
+     *                 sub-animations are properly coordinated. This duration should not actually
+     *                 be used since this animation tracks a swipe progress.
+     */
+    protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
+        AnimatorSet fullAnimation = new AnimatorSet();
+        fullAnimation.setDuration(duration);
+
+        TaskbarUIController uiController = mControllers.uiController;
+        if (uiController instanceof LauncherTaskbarUIController) {
+            ((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation(
+                    fullAnimation, duration);
+        }
+        mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration);
+
+        if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
+            ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
+            alphaOverride.setDuration(duration);
+            alphaOverride.addUpdateListener(a -> {
+                // Override the alpha updates in the icon alignment animation.
+                mControllers.taskbarViewController.getAllAppsButtonView().setAlpha(0);
+            });
+            fullAnimation.play(alphaOverride);
+        }
+
+        return AnimatorPlaybackController.wrap(fullAnimation, duration);
+    }
+
+    /**
      * Called when we determine the touchable region.
      *
      * @param exclude {@code true} then the magnification region computation will omit the window.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 9870a2e..21d7af9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -16,10 +16,11 @@
 package com.android.launcher3.taskbar
 
 import android.graphics.Insets
+import android.graphics.Region
+import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
 import android.view.WindowManager
 import com.android.launcher3.AbstractFloatingView
 import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
-import com.android.launcher3.R
 import com.android.launcher3.anim.AlphaUpdateListener
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
 import com.android.quickstep.KtR
@@ -36,6 +37,7 @@
     /** The bottom insets taskbar provides to the IME when IME is visible. */
     val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(
         KtR.dimen.taskbar_ime_size)
+    private val contentRegion: Region = Region()
 
     // Initialized in init.
     private lateinit var controllers: TaskbarControllers
@@ -50,7 +52,8 @@
             windowLayoutParams,
             intArrayOf(
                 ITYPE_EXTRA_NAVIGATION_BAR,
-                ITYPE_BOTTOM_TAPPABLE_ELEMENT
+                ITYPE_BOTTOM_TAPPABLE_ELEMENT,
+                ITYPE_BOTTOM_MANDATORY_GESTURES
             )
         )
 
@@ -67,14 +70,20 @@
     fun onTaskbarWindowHeightOrInsetsChanged() {
         var reducingSize = getReducingInsetsForTaskbarInsetsHeight(
             controllers.taskbarStashController.contentHeightToReportToApps)
+
+        contentRegion.set(0, reducingSize.top,
+                context.dragLayer.width, windowLayoutParams.height)
         windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
+        windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
         reducingSize = getReducingInsetsForTaskbarInsetsHeight(
             controllers.taskbarStashController.tappableHeightToReportToApps)
         windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
+        windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
 
         reducingSize = getReducingInsetsForTaskbarInsetsHeight(taskbarHeightForIme)
         windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
         windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
+        windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
     }
 
     /**
@@ -121,7 +130,8 @@
                 if (context.isTaskbarWindowFullscreen) {
                     InsetsInfo.TOUCHABLE_INSETS_FRAME
                 } else {
-                    InsetsInfo.TOUCHABLE_INSETS_CONTENT
+                    insetsInfo.touchableRegion.set(contentRegion)
+                    InsetsInfo.TOUCHABLE_INSETS_REGION
                 }
             )
             insetsIsTouchableRegion = false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 2e37170..ef7bab9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.util.DisplayController;
@@ -183,6 +184,17 @@
     }
 
     /**
+     * Displays a frame of the first Launcher reveal animation.
+     *
+     * This should be used to run a first Launcher reveal animation whose progress matches a swipe
+     * progress.
+     */
+    public AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
+        return mTaskbarActivityContext == null
+                ? null : mTaskbarActivityContext.createLauncherStartFromSuwAnim(duration);
+    }
+
+    /**
      * Called when the user is unlocked
      */
     public void onUserUnlocked() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 7d95743..fc9f9d0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -35,6 +35,8 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
@@ -368,12 +370,33 @@
     }
 
     /**
+     * Adds the Taskbar unstash to Hotseat animator to the animator set.
+     *
+     * This should be used to run a Taskbar unstash to Hotseat animation whose progress matches a
+     * swipe progress.
+     *
+     * @param placeholderDuration a placeholder duration to be used to ensure all full-length
+     *                            sub-animations are properly coordinated. This duration should not
+     *                            actually be used since this animation tracks a swipe progress.
+     */
+    protected void addUnstashToHotseatAnimation(AnimatorSet animation, int placeholderDuration) {
+        createAnimToIsStashed(
+                /* isStashed= */ false,
+                placeholderDuration,
+                /* startDelay= */ 0,
+                /* animateBg= */ false);
+        animation.play(mAnimator);
+    }
+
+    /**
      * Create a stash animation and save to {@link #mAnimator}.
      * @param isStashed whether it's a stash animation or an unstash animation
      * @param duration duration of the animation
      * @param startDelay how many milliseconds to delay the animation after starting it.
+     * @param animateBg whether the taskbar's background should be animated
      */
-    private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay) {
+    private void createAnimToIsStashed(
+            boolean isStashed, long duration, long startDelay, boolean animateBg) {
         if (mAnimator != null) {
             mAnimator.cancel();
         }
@@ -408,10 +431,14 @@
             secondHalfDurationScale = 0.5f;
             final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
 
-            fullLengthAnimatorSet.playTogether(
-                    mTaskbarBackgroundOffset.animateToValue(1),
-                    mIconTranslationYForStash.animateToValue(stashTranslation)
-            );
+            fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation));
+            if (animateBg) {
+                fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(1));
+            } else {
+                fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback(
+                        () -> mTaskbarBackgroundOffset.updateValue(1)));
+            }
+
             firstHalfAnimatorSet.playTogether(
                     mIconAlphaForStash.animateToValue(0),
                     mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
@@ -424,10 +451,15 @@
             secondHalfDurationScale = 0.75f;
 
             fullLengthAnimatorSet.playTogether(
-                    mTaskbarBackgroundOffset.animateToValue(0),
                     mIconScaleForStash.animateToValue(1),
-                    mIconTranslationYForStash.animateToValue(0)
-            );
+                    mIconTranslationYForStash.animateToValue(0));
+            if (animateBg) {
+                fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(0));
+            } else {
+                fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback(
+                        () -> mTaskbarBackgroundOffset.updateValue(0)));
+            }
+
             firstHalfAnimatorSet.playTogether(
                     mTaskbarStashedHandleAlpha.animateToValue(0)
             );
@@ -728,7 +760,7 @@
                 mIsStashed = isStashed;
 
                 // This sets mAnimator.
-                createAnimToIsStashed(mIsStashed, duration, startDelay);
+                createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
                 if (start) {
                     mAnimator.start();
                 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 5db495d..3dd7932 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -258,21 +258,21 @@
         setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
                 anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
 
-        int count = mTaskbarView.getChildCount();
-        for (int i = 0; i < count; i++) {
+        for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
             View child = mTaskbarView.getChildAt(i);
 
-            int positionInHotseat = -1;
-            boolean isRtl = Utilities.isRtl(child.getResources());
+            int positionInHotseat;
             if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
-                    && ((isRtl && i == 0) || (!isRtl && i == count - 1))) {
+                    && child == mTaskbarView.getAllAppsButtonView()) {
                 // Note that there is no All Apps button in the hotseat, this position is only used
                 // as its convenient for animation purposes.
-                positionInHotseat = isRtl
+                positionInHotseat = Utilities.isRtl(child.getResources())
                         ? -1
                         : mActivity.getDeviceProfile().numShownHotseatIcons;
 
-                setter.setViewAlpha(child, 0, LINEAR);
+                if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
+                    setter.setViewAlpha(child, 0, LINEAR);
+                }
             } else if (child.getTag() instanceof ItemInfo) {
                 positionInHotseat = ((ItemInfo) child.getTag()).screenId;
             } else {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index f60b225..21728ad 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -151,6 +151,7 @@
 
     // Null if the recents animation hasn't started yet or has been canceled or finished.
     protected @Nullable RecentsAnimationController mRecentsAnimationController;
+    protected @Nullable RecentsAnimationController mDeferredCleanupRecentsAnimationController;
     protected RecentsAnimationTargets mRecentsAnimationTargets;
     protected T mActivity;
     protected Q mRecentsView;
@@ -435,6 +436,9 @@
                     mRecentsView.switchToScreenshot(snapshots, () -> {
                         if (mRecentsAnimationController != null) {
                             mRecentsAnimationController.cleanupScreenshot();
+                        } else if (mDeferredCleanupRecentsAnimationController != null) {
+                            mDeferredCleanupRecentsAnimationController.cleanupScreenshot();
+                            mDeferredCleanupRecentsAnimationController = null;
                         }
                     });
                     mRecentsView.onRecentsAnimationComplete();
@@ -839,6 +843,9 @@
     public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
         mActivityInitListener.unregister();
+        // Cache the recents animation controller so we can defer its cleanup to after having
+        // properly cleaned up the screenshot without accidentally using it.
+        mDeferredCleanupRecentsAnimationController = mRecentsAnimationController;
         mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
 
         if (mRecentsAnimationTargets != null) {
@@ -1025,19 +1032,19 @@
             return RECENTS;
         }
         final GestureEndTarget endTarget;
-        final boolean canGoToNewTask;
+        final boolean goingToNewTask;
         if (mRecentsView != null) {
             if (!hasTargets()) {
                 // If there are no running tasks, then we can assume that this is a continuation of
                 // the last gesture, but after the recents animation has finished
-                canGoToNewTask = true;
+                goingToNewTask = true;
             } else {
                 final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
                 final int taskToLaunch = mRecentsView.getNextPage();
-                canGoToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
+                goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
             }
         } else {
-            canGoToNewTask = false;
+            goingToNewTask = false;
         }
         final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
         final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
@@ -1046,13 +1053,13 @@
             if (isCancel) {
                 endTarget = LAST_TASK;
             } else if (mDeviceState.isFullyGesturalNavMode()) {
-                if (canGoToNewTask && isFlingX) {
+                if (goingToNewTask && isFlingX) {
                     // Flinging towards new task takes precedence over mIsMotionPaused (which only
                     // checks y-velocity).
                     endTarget = NEW_TASK;
                 } else if (mIsMotionPaused) {
                     endTarget = RECENTS;
-                } else if (canGoToNewTask) {
+                } else if (goingToNewTask) {
                     endTarget = NEW_TASK;
                 } else {
                     endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME;
@@ -1060,22 +1067,26 @@
             } else {
                 endTarget = reachedOverviewThreshold && mGestureStarted
                         ? RECENTS
-                        : canGoToNewTask
+                        : goingToNewTask
                                 ? NEW_TASK
                                 : LAST_TASK;
             }
         } else {
             // If swiping at a diagonal, base end target on the faster velocity.
             boolean isSwipeUp = endVelocity < 0;
-            boolean willGoToNewTask =
-                    canGoToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
+            boolean willGoToNewTaskOnSwipeUp =
+                    goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
 
-            if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
-                endTarget = willGoToNewTask ? NEW_TASK : HOME;
+            if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+                endTarget = HOME;
+            } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
+                // If swiping at a diagonal, base end target on the faster velocity.
+                endTarget = NEW_TASK;
             } else if (isSwipeUp) {
-                endTarget = (!reachedOverviewThreshold && willGoToNewTask) ? NEW_TASK : RECENTS;
+                endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
+                        ? NEW_TASK : RECENTS;
             } else {
-                endTarget = willGoToNewTask ? NEW_TASK : LAST_TASK; // Swipe is downward.
+                endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
             }
         }
 
@@ -1160,8 +1171,6 @@
                     duration = Math.max(duration, mRecentsView.getScroller().getDuration());
                 }
             }
-        } else if (endTarget == LAST_TASK && mRecentsView != null) {
-            mRecentsView.snapToPage(mRecentsView.getCurrentPage(), Math.toIntExact(duration));
         }
 
         // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 80bc329..39c5f2a 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
@@ -183,30 +184,31 @@
      */
     public static class CachedTaskInfo {
 
+        @Nullable
         private final RunningTaskInfo mTopTask;
         private final List<RunningTaskInfo> mAllCachedTasks;
 
         CachedTaskInfo(List<RunningTaskInfo> allCachedTasks) {
             mAllCachedTasks = allCachedTasks;
-            mTopTask = allCachedTasks.get(0);
+            mTopTask = allCachedTasks.isEmpty() ? null : allCachedTasks.get(0);
         }
 
         public int getTaskId() {
-            return mTopTask.taskId;
+            return mTopTask == null ? -1 : mTopTask.taskId;
         }
 
         /**
          * Returns true if the root of the task chooser activity
          */
         public boolean isRootChooseActivity() {
-            return ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction());
+            return mTopTask != null && ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction());
         }
 
         /**
          * Returns true if the given task holds an Assistant activity that is excluded from recents
          */
         public boolean isExcludedAssistant() {
-            return mTopTask.configuration.windowConfiguration
+            return mTopTask != null && mTopTask.configuration.windowConfiguration
                     .getActivityType() == ACTIVITY_TYPE_ASSISTANT
                     && (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
         }
@@ -215,7 +217,7 @@
          * Returns true if this represents the HOME task
          */
         public boolean isHomeTask() {
-            return mTopTask.configuration.windowConfiguration
+            return mTopTask != null && mTopTask.configuration.windowConfiguration
                     .getActivityType() == ACTIVITY_TYPE_HOME;
         }
 
@@ -224,7 +226,8 @@
          * is loaded by the model
          */
         public Task[] getPlaceholderTasks() {
-            return new Task[] {Task.from(new TaskKey(mTopTask), mTopTask, false)};
+            return mTopTask == null ? new Task[0]
+                    : new Task[] {Task.from(new TaskKey(mTopTask), mTopTask, false)};
         }
 
         /**
@@ -232,6 +235,9 @@
          * placeholder until the true object is loaded by the model
          */
         public Task[] getPlaceholderTasks(int[] taskIds) {
+            if (mTopTask == null) {
+                return new Task[0];
+            }
             Task[] result = new Task[taskIds.length];
             for (int i = 0; i < taskIds.length; i++) {
                 final int index = i;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 3e68c7f..ab3201a 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -56,6 +56,7 @@
 public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
         implements StateListener<RecentsState> {
 
+    @Nullable
     private Task mHomeTask;
 
     public FallbackRecentsView(Context context, AttributeSet attrs) {
@@ -89,7 +90,7 @@
             RotationTouchHelper rotationTouchHelper) {
         // TODO(b/195607777) General fallback love, but this might be correct
         //  Home task should be defined as the front-most task info I think?
-        mHomeTask = homeTask[0];
+        mHomeTask = homeTask.length > 0 ? homeTask[0] : null;
         onGestureAnimationStart(homeTask, rotationTouchHelper);
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 269b3c2..a379aad 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.Utilities.mapBoundToRange;
 import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import android.animation.Animator;
@@ -54,6 +55,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.Executors;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
@@ -78,6 +80,8 @@
 
     private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
 
+    private static final int MAX_SWIPE_DURATION = 350;
+
     private TISBindHelper mTISBindHelper;
     private TISBinder mBinder;
 
@@ -90,6 +94,8 @@
     private LottieAnimationView mAnimatedBackground;
     private Animator.AnimatorListener mBackgroundAnimatorListener;
 
+    private AnimatorPlaybackController mLauncherStartAnim = null;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -237,11 +243,20 @@
 
     private void onSwipeProgressUpdate() {
         mBackground.setProgress(mSwipeProgress.value);
-        float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR,
-                1, 0, LINEAR);
+        float alpha = Utilities.mapBoundToRange(
+                mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, 1, 0, LINEAR);
         mContentView.setAlpha(alpha);
         mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
 
+        if (mLauncherStartAnim == null) {
+            mLauncherStartAnim = mBinder.getTaskbarManager().createLauncherStartFromSuwAnim(
+                    MAX_SWIPE_DURATION);
+        }
+        if (mLauncherStartAnim != null) {
+            mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange(
+                    mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN));
+        }
+
         if (alpha == 0f) {
             mAnimatedBackground.pauseAnimation();
         } else if (!mAnimatedBackground.isAnimating()) {
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 3b1c150..4757d4b 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -53,6 +53,7 @@
         mTargetViewRootImpl = targetView.getViewRootImpl();
         mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
         mApplyHandler = new Handler(this::onApplyMessage);
+        setCanRelease(true);
     }
 
     protected boolean onApplyMessage(Message msg) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b634518..2360396 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2257,6 +2257,9 @@
      * is called.  Also scrolls the view to this task.
      */
     private void showCurrentTask(Task[] runningTasks) {
+        if (runningTasks.length == 0) {
+            return;
+        }
         int runningTaskViewId = -1;
         boolean needGroupTaskView = runningTasks.length > 1;
         if (shouldAddStubTaskView(runningTasks)) {
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index dd99a7a..2612a7d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,13 +57,18 @@
     <dimen name="workspace_page_indicator_line_height">1dp</dimen>
     <dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
 
-<!-- Drop target bar -->
+    <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">56dp</dimen>
     <dimen name="drop_target_vertical_gap">20dp</dimen>
     <dimen name="drop_target_top_margin">32dp</dimen>
     <dimen name="drop_target_bottom_margin">16dp</dimen>
 
-<!-- App Widget resize frame -->
+    <!-- App Widget resize frame -->
+    <!-- Button drop target bar -->
+    <dimen name="button_drop_target_min_text_size">10sp</dimen>
+    <dimen name="button_drop_target_resize_text_increment">1sp</dimen>
+
+    <!-- App Widget resize frame -->
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
     <dimen name="resize_frame_margin">22dp</dimen>
@@ -76,7 +81,7 @@
     <dimen name="widget_reconfigure_button_size">36dp</dimen>
     <dimen name="widget_reconfigure_tip_top_margin">16dp</dimen>
 
-<!-- Fast scroll -->
+    <!-- Fast scroll -->
     <dimen name="fastscroll_track_min_width">6dp</dimen>
     <dimen name="fastscroll_track_max_width">8dp</dimen>
     <dimen name="fastscroll_thumb_padding">1dp</dimen>
@@ -136,19 +141,20 @@
     <dimen name="all_apps_paged_view_top_padding">40dp</dimen>
     <dimen name="all_apps_personal_work_tabs_vertical_margin">16dp</dimen>
 
+    <dimen name="all_apps_icon_drawable_padding">8dp</dimen>
     <!-- The size of corner radius of the arrow in the arrow toast. -->
     <dimen name="arrow_toast_corner_radius">2dp</dimen>
     <dimen name="arrow_toast_elevation">2dp</dimen>
     <dimen name="arrow_toast_arrow_width">10dp</dimen>
 
-<!-- Search bar in All Apps -->
+    <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
     <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
     <dimen name="all_apps_header_shadow_height">6dp</dimen>
 
     <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
-<!-- Floating action button inside work tab to toggle work profile -->
+    <!-- Floating action button inside work tab to toggle work profile -->
     <dimen name="work_fab_height">56dp</dimen>
     <dimen name="work_fab_radius">16dp</dimen>
     <dimen name="work_card_padding_horizontal">10dp</dimen>
@@ -219,13 +225,13 @@
     <dimen name="shortcut_preview_padding_right">0dp</dimen>
     <dimen name="shortcut_preview_padding_top">0dp</dimen>
 
-<!-- Pin widget dialog -->
+    <!-- Pin widget dialog -->
     <dimen name="pin_widget_button_padding_horizontal">8dp</dimen>
     <dimen name="pin_widget_button_padding_vertical">4dp</dimen>
     <dimen name="pin_widget_button_inset_horizontal">4dp</dimen>
     <dimen name="pin_widget_button_inset_vertical">6dp</dimen>
 
-<!-- Dragging -->
+    <!-- Dragging -->
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">14dp</dimen>
     <dimen name="drop_target_text_size">16sp</dimen>
@@ -250,7 +256,7 @@
     <dimen name="spring_loaded_panel_border">2dp</dimen>
     <dimen name="keyboard_drag_stroke_width">4dp</dimen>
 
-<!-- Folders -->
+    <!-- Folders -->
     <dimen name="page_indicator_dot_size">8dp</dimen>
 
     <dimen name="folder_cell_x_padding">9dp</dimen>
@@ -262,22 +268,22 @@
     <dimen name="folder_content_padding_left_right">8dp</dimen>
     <dimen name="folder_content_padding_top">16dp</dimen>
 
-<!-- Sizes for managed profile badges -->
+    <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
     <dimen name="profile_badge_margin">5dp</dimen>
     <dimen name="profile_badge_minimum_top">2dp</dimen>
 
-<!-- Shadows and outlines -->
+    <!-- Shadows and outlines -->
     <dimen name="blur_size_thin_outline">1dp</dimen>
     <dimen name="blur_size_medium_outline">2dp</dimen>
     <dimen name="blur_size_click_shadow">4dp</dimen>
     <dimen name="click_shadow_high_shift">2dp</dimen>
 
-<!-- Pending widget -->
+    <!-- Pending widget -->
     <dimen name="pending_widget_min_padding">8dp</dimen>
     <dimen name="pending_widget_elevation">2dp</dimen>
 
-<!-- Deep shortcuts -->
+    <!-- Deep shortcuts -->
     <dimen name="deep_shortcuts_elevation">2dp</dimen>
     <dimen name="bg_popup_padding">2dp</dimen>
     <dimen name="bg_popup_item_width">216dp</dimen>
@@ -311,7 +317,7 @@
     <!-- (system_shortcut_header_icon_touch_size - system_shortcut_icon_size) / 2 -->
     <dimen name="system_shortcut_header_icon_padding">14dp</dimen>
 
-<!-- Notifications -->
+    <!-- Notifications -->
     <dimen name="bg_round_rect_radius">8dp</dimen>
     <dimen name="notification_max_trans">8dp</dimen>
     <dimen name="notification_space">8dp</dimen>
@@ -329,11 +335,11 @@
     <dimen name="notification_main_text_padding_start">56dp</dimen>
     <dimen name="horizontal_ellipsis_size">18dp</dimen>
 
-<!-- Overview -->
+    <!-- Overview -->
     <dimen name="options_menu_icon_size">24dp</dimen>
     <dimen name="options_menu_thumb_size">32dp</dimen>
 
-<!-- Snackbar -->
+    <!-- Snackbar -->
     <dimen name="snackbar_height">48dp</dimen>
     <dimen name="snackbar_content_height">32dp</dimen>
     <dimen name="snackbar_padding">8dp</dimen>
@@ -345,17 +351,17 @@
     <dimen name="snackbar_max_text_size">14sp</dimen>
     <dimen name="snackbar_max_width">504dp</dimen>
 
-<!-- Developer Options -->
+    <!-- Developer Options -->
     <dimen name="developer_options_filter_margins">10dp</dimen>
 
-<!-- Theming related -->
+    <!-- Theming related -->
     <dimen name="default_dialog_corner_radius">8dp</dimen>
     <dimen name="dialogCornerRadius">@dimen/default_dialog_corner_radius</dimen>
 
     <!-- Onboarding bottomsheet related -->
     <dimen name="bottom_sheet_edu_padding">24dp</dimen>
 
-<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
+    <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="taskbar_size">0dp</dimen>
     <dimen name="taskbar_stashed_size">0dp</dimen>
     <dimen name="qsb_widget_height">0dp</dimen>
@@ -366,10 +372,10 @@
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
 
-<!-- Base Swipe Detector, speed in dp/s -->
+    <!-- Base Swipe Detector, speed in dp/s -->
     <dimen name="base_swift_detector_fling_release_velocity">1dp</dimen>
 
-<!-- Overview placeholder to compile in Launcher3 without Quickstep -->
+    <!-- Overview placeholder to compile in Launcher3 without Quickstep -->
     <dimen name="task_thumbnail_icon_size">0dp</dimen>
     <dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
     <dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
@@ -388,17 +394,17 @@
     <dimen name="task_menu_width_grid">216dp</dimen>
 
 
-<!-- Workspace grid visualization parameters -->
+    <!-- Workspace grid visualization parameters -->
     <dimen name="grid_visualization_rounding_radius">28dp</dimen>
     <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
     <dimen name="grid_visualization_vertical_cell_spacing">6dp</dimen>
 
-<!-- Search results related parameters -->
+    <!-- Search results related parameters -->
     <dimen name="search_row_icon_size">48dp</dimen>
     <dimen name="search_row_small_icon_size">32dp</dimen>
     <dimen name="padded_rounded_button_padding">8dp</dimen>
 
-<!-- Bottom sheet related parameters -->
+    <!-- Bottom sheet related parameters -->
     <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
     <dimen name="bottom_sheet_handle_width">32dp</dimen>
     <dimen name="bottom_sheet_handle_height">4dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 829a21d..847e4a8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -438,6 +438,10 @@
     <!-- A hint shown in launcher settings develop options filter box -->
     <string name="developer_options_filter_hint">Filter</string>
 
+    <!-- Title for preference screen show in Home Settings related to smart search preferences. [CHAR LIMIT=50]-->
+    <string name="search_pref_screen_title">Search your phone</string>
+    <!-- Title for preference screen show in Home Settings related to smart search preferences. [CHAR LIMIT=50]-->
+    <string name="search_pref_screen_title_tablet">Search your tablet</string>
     <!-- Failed action error message: e.g. Failed: Pause -->
     <string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
 </resources>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 290bc8c..0802552 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -34,6 +34,8 @@
             launcher:minHeightDps="300"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -42,6 +44,8 @@
             launcher:minHeightDps="400"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
     </grid-option>
@@ -63,6 +67,8 @@
             launcher:minHeightDps="420"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -71,6 +77,8 @@
             launcher:minHeightDps="450"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -79,6 +87,8 @@
             launcher:minHeightDps="491.33"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -87,6 +97,8 @@
             launcher:minHeightDps="567"
             launcher:iconImageSize="54"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -95,6 +107,8 @@
             launcher:minHeightDps="567"
             launcher:iconImageSize="54"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
     </grid-option>
@@ -116,6 +130,8 @@
             launcher:minHeightDps="694"
             launcher:iconImageSize="56"
             launcher:iconTextSize="14.4"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -124,6 +140,8 @@
             launcher:minHeightDps="694"
             launcher:iconImageSize="56"
             launcher:iconTextSize="14.4"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
         <display-option
@@ -132,6 +150,8 @@
             launcher:minHeightDps="400"
             launcher:iconImageSize="48"
             launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
             launcher:canBeDefault="true" />
 
     </grid-option>
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 477964a..371bb80 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -160,7 +160,7 @@
         // Remove the item from launcher and the db, we can ignore the containerInfo in this call
         // because we already remove the drag view from the folder (if the drag originated from
         // a folder) in Folder.beginDrag()
-        mLauncher.removeItem(view, item, true /* deleteFromDb */);
+        mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop");
         mLauncher.getWorkspace().stripEmptyScreens();
         mLauncher.getDragLayer()
                 .announceForAccessibility(getContext().getString(R.string.item_removed));
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index f028d3c..5152217 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -255,7 +255,6 @@
         mInsets.set(windowBounds.insets);
 
         isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
-
         // Determine device posture.
         mInfo = info;
         isTablet = info.isTablet(windowBounds);
@@ -265,8 +264,8 @@
 
         // Some more constants.
         context = getContext(context, info, isVerticalBarLayout() || (isTablet && isLandscape)
-                ? Configuration.ORIENTATION_LANDSCAPE
-                : Configuration.ORIENTATION_PORTRAIT,
+                        ? Configuration.ORIENTATION_LANDSCAPE
+                        : Configuration.ORIENTATION_PORTRAIT,
                 windowBounds);
         final Resources res = context.getResources();
         mMetrics = res.getDisplayMetrics();
@@ -313,7 +312,6 @@
         allAppsShiftRange = isTablet
                 ? heightPx - allAppsTopPadding
                 : res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
-
         folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
         folderContentPaddingLeftRight =
                 res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
@@ -795,27 +793,26 @@
         allAppsBorderSpacePx = new Point(
                 pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics, scale),
                 pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics, scale));
+        // AllApps cells don't have real space between cells,
+        // so we add the border space to the cell height
+        allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics, scale)
+                + allAppsBorderSpacePx.y;
+        // but width is just the cell,
+        // the border is added in #updateAllAppsContainerWidth
+        allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
         if (isScalableGrid) {
             allAppsIconSizePx =
                     pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
             allAppsIconTextSizePx =
                     pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
-            // AllApps cells don't have real space between cells,
-            // so we add the border space to the cell height
-            allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics, scale)
-                    + allAppsBorderSpacePx.y;
-            // but width is just the cell,
-            // the border is added in #updateAllAppsContainerWidth
-            allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
         } else {
-            float invIconSizeDp = inv.iconSize[mTypeIndex];
-            float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
+            float invIconSizeDp = inv.allAppsIconSize[mTypeIndex];
+            float invIconTextSizeSp = inv.allAppsIconTextSize[mTypeIndex];
             allAppsIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
             allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale);
-            allAppsIconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
-            allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
-            allAppsCellHeightPx = getCellSize().y;
+            allAppsIconDrawablePaddingPx =
+                    res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
         }
 
         updateAllAppsContainerWidth(res);
@@ -1145,7 +1142,7 @@
             return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace);
         } else {
             return (int) (freeSpace * mQsbCenterFactor)
-                + (isTaskbarPresent ? taskbarSize : mInsets.bottom);
+                    + (isTaskbarPresent ? taskbarSize : mInsets.bottom);
         }
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 135b88d..31c1eff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1956,6 +1956,19 @@
      * @param deleteFromDb whether or not to delete this item from the db.
      */
     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
+        return removeItem(v, itemInfo, deleteFromDb, null);
+    }
+
+    /**
+     * Unbinds the view for the specified item, and removes the item and all its children.
+     *
+     * @param v the view being removed.
+     * @param itemInfo the {@link ItemInfo} for this view.
+     * @param deleteFromDb whether or not to delete this item from the db.
+     * @param reason the resaon for removal.
+     */
+    public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb,
+            @Nullable final String reason) {
         if (itemInfo instanceof WorkspaceItemInfo) {
             // Remove the shortcut from the folder before removing it from launcher
             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
@@ -1965,7 +1978,7 @@
                 mWorkspace.removeWorkspaceItem(v);
             }
             if (deleteFromDb) {
-                getModelWriter().deleteItemFromDatabase(itemInfo);
+                getModelWriter().deleteItemFromDatabase(itemInfo, reason);
             }
         } else if (itemInfo instanceof FolderInfo) {
             final FolderInfo folderInfo = (FolderInfo) itemInfo;
@@ -1980,7 +1993,7 @@
             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
-                getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost());
+                getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason);
             }
         } else {
             return false;
@@ -2032,13 +2045,16 @@
         // Note: There should be at most one log per method call. This is enforced implicitly
         // by using if-else statements.
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
-        if (topView != null && topView.onBackPressed()) {
-            // Handled by the floating view.
-        } else {
-            mStateManager.getState().onBackPressed(this);
+        if (topView == null || !topView.onBackPressed()) {
+            // Not handled by the floating view.
+            onStateBack();
         }
     }
 
+    protected void onStateBack() {
+        mStateManager.getState().onBackPressed(this);
+    }
+
     protected void onScreenOff() {
         // Reset AllApps to its initial state only if we are not in the middle of
         // processing a multi-step drop
@@ -2399,8 +2415,7 @@
                     if (FeatureFlags.IS_STUDIO_BUILD) {
                         throw (new RuntimeException(desc));
                     } else {
-                        Log.d(TAG, desc);
-                        getModelWriter().deleteItemFromDatabase(item);
+                        getModelWriter().deleteItemFromDatabase(item, desc);
                         if (TestProtocol.sDebugTracing) {
                             Log.d(TestProtocol.MISSING_PROMISE_ICON,
                                     TAG + "bindItems failed for item=" + item);
@@ -2480,7 +2495,8 @@
         if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) {
             item.providerName = QsbContainerView.getSearchComponentName(this);
             if (item.providerName == null) {
-                getModelWriter().deleteItemFromDatabase(item);
+                getModelWriter().deleteItemFromDatabase(item,
+                        "search widget removed because search component cannot be found");
                 return null;
             }
         }
@@ -2531,10 +2547,10 @@
             if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
                     && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
                 if (appWidgetInfo == null) {
-                    FileLog.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+                    getModelWriter().deleteItemFromDatabase(item,
+                            "Removing restored widget: id=" + item.appWidgetId
                             + " belongs to component " + item.providerName + " user " + item.user
                             + ", as the provider is null and " + removalReason);
-                    getModelWriter().deleteItemFromDatabase(item);
                     return null;
                 }
 
@@ -2604,7 +2620,7 @@
                 // Verify that we own the widget
                 if (appWidgetInfo == null) {
                     FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
-                    getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
+                    getModelWriter().deleteWidgetInfo(item, getAppWidgetHost(), removalReason);
                     return null;
                 }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5c5c101..de0d300 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -477,7 +477,9 @@
                 }
 
                 if (!removedIds.isEmpty()) {
-                    deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
+                    deleteAndBindComponentsRemoved(
+                            ItemInfoMatcher.ofItemIds(removedIds),
+                            "removed because install session failed");
                 }
             }
         });
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6c091f0..93f3d9f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3068,7 +3068,8 @@
             if (info instanceof LauncherAppWidgetInfo) {
                 LauncherAppWidgetInfo appWidgetInfo = (LauncherAppWidgetInfo) info;
                 if (appWidgetInfo.appWidgetId == appWidgetId) {
-                    mLauncher.removeItem(view, appWidgetInfo, true);
+                    mLauncher.removeItem(view, appWidgetInfo, true,
+                            "widget is removed in response to widget remove broadcast");
                     return true;
                 }
             }
@@ -3318,8 +3319,9 @@
      *
      * @param matcher  the matcher generated by the caller.
      */
-    public void persistRemoveItemsByMatcher(Predicate<ItemInfo> matcher) {
-        mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
+    public void persistRemoveItemsByMatcher(Predicate<ItemInfo> matcher,
+            @Nullable final String reason) {
+        mLauncher.getModelWriter().deleteItemsFromDatabase(matcher, reason);
         removeItemsByMatcher(matcher);
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 096e2c8..88e7fc0 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,9 +15,7 @@
  */
 package com.android.launcher3.allapps;
 
-import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
@@ -47,7 +45,6 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -97,8 +94,6 @@
     private AllAppsBackgroundDrawable mEmptySearchBackground;
     private int mEmptySearchBackgroundTopOffset;
 
-    private ArrayList<View> mAutoSizedOverlays = new ArrayList<>();
-
     public AllAppsRecyclerView(Context context) {
         this(context, null);
     }
@@ -172,30 +167,6 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         updateEmptySearchBackgroundBounds();
         updatePoolSize();
-        for (int i = 0; i < mAutoSizedOverlays.size(); i++) {
-            View overlay = mAutoSizedOverlays.get(i);
-            overlay.measure(makeMeasureSpec(w, EXACTLY), makeMeasureSpec(w, EXACTLY));
-            overlay.layout(0, 0, w, h);
-        }
-    }
-
-    /**
-     * Adds an overlay that automatically rescales with the recyclerview.
-     */
-    public void addAutoSizedOverlay(View overlay) {
-        mAutoSizedOverlays.add(overlay);
-        getOverlay().add(overlay);
-        onSizeChanged(getWidth(), getHeight(), getWidth(), getHeight());
-    }
-
-    /**
-     * Clears auto scaling overlay views added by #addAutoSizedOverlay
-     */
-    public void clearAutoSizedOverlays() {
-        for (View v : mAutoSizedOverlays) {
-            getOverlay().remove(v);
-        }
-        mAutoSizedOverlays.clear();
     }
 
     public void onSearchResultsChanged() {
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index 6196df2..a312070 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,8 +19,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 
-import androidx.annotation.CallSuper;
-
 /**
  * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
  */
@@ -29,7 +27,6 @@
     protected boolean mCancelled = false;
 
     @Override
-    @CallSuper
     public void onAnimationCancel(Animator animation) {
         mCancelled = true;
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 54edb33..9775b87 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -246,6 +246,10 @@
             "ENABLE_ALL_APPS_IN_TASKBAR", true,
             "Enables accessing All Apps from the system Taskbar.");
 
+    public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(
+            "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false,
+            "Enables displaying the all apps button in the hotseat.");
+
     public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(
             "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
             "Enables One Search box in Taskbar All Apps.");
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index c5b3913..1f0a011 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -100,7 +100,8 @@
                     }
 
                     // Remove the folder
-                    mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */);
+                    mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */,
+                            "folder removed because there's only 1 item in it");
                     if (folder.mFolderIcon instanceof DropTarget) {
                         folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon);
                     }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2b6e426..f4f270c 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -731,7 +731,8 @@
         enum LatencyType {
             UNKNOWN(0),
             COLD(1),
-            HOT(2);
+            HOT(2),
+            TIMEOUT(3);
 
             private final int mId;
 
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 6c4cfb9..b50ab58 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -43,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -203,7 +204,9 @@
         }
 
         private void bind() {
-            IntSet currentScreenIds = mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
+            final IntSet currentScreenIds =
+                    mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
+            Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks);
 
             // Separate the items that are on the current screen, and all the other remaining items
             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 832c1dd..2a6a691 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,6 +17,8 @@
 
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.CallbackTask;
@@ -128,8 +130,9 @@
         scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
     }
 
-    public void deleteAndBindComponentsRemoved(final Predicate<ItemInfo> matcher) {
-        getModelWriter().deleteItemsFromDatabase(matcher);
+    public void deleteAndBindComponentsRemoved(final Predicate<ItemInfo> matcher,
+            @Nullable final String reason) {
+        getModelWriter().deleteItemsFromDatabase(matcher, reason);
 
         // Call the components-removed callback
         scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher));
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index d52537e..de23c4b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -31,6 +31,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings;
@@ -469,6 +470,7 @@
          * or an empty IntSet
          * @param orderedScreenIds All the page ids to be bound
          */
+        @NonNull
         default IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
             return new IntSet();
         }
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index df6768d..422af43 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -51,7 +51,7 @@
      * specified screen.
      */
     public static <T extends ItemInfo> void filterCurrentWorkspaceItems(
-            IntSet currentScreenIds,
+            final IntSet currentScreenIds,
             ArrayList<T> allWorkspaceItems,
             ArrayList<T> currentScreenItems,
             ArrayList<T> otherScreenItems) {
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 015abe9..0a68d4a 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -23,8 +23,10 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
+import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
@@ -272,27 +274,30 @@
     /**
      * Removes the specified item from the database
      */
-    public void deleteItemFromDatabase(ItemInfo item) {
-        deleteItemsFromDatabase(Arrays.asList(item));
+    public void deleteItemFromDatabase(ItemInfo item, @Nullable final String reason) {
+        deleteItemsFromDatabase(Arrays.asList(item), reason);
     }
 
     /**
      * Removes all the items from the database matching {@param matcher}.
      */
-    public void deleteItemsFromDatabase(Predicate<ItemInfo> matcher) {
+    public void deleteItemsFromDatabase(@NonNull final Predicate<ItemInfo> matcher,
+            @Nullable final String reason) {
         deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
-                        .filter(matcher).collect(Collectors.toList()));
+                        .filter(matcher).collect(Collectors.toList()), reason);
     }
 
     /**
      * Removes the specified items from the database
      */
-    public void deleteItemsFromDatabase(final Collection<? extends ItemInfo> items) {
+    public void deleteItemsFromDatabase(final Collection<? extends ItemInfo> items,
+            @Nullable final String reason) {
         ModelVerifier verifier = new ModelVerifier();
         FileLog.d(TAG, "removing items from db " + items.stream().map(
                 (item) -> item.getTargetComponent() == null ? ""
                         : item.getTargetComponent().getPackageName()).collect(
-                Collectors.joining(",")));
+                Collectors.joining(","))
+                + ". Reason: [" + (TextUtils.isEmpty(reason) ? "unknown" : reason) + "]");
         notifyDelete(items);
         enqueueDeleteRunnable(() -> {
             for (ItemInfo item : items) {
@@ -328,14 +333,15 @@
     /**
      * Deletes the widget info and the widget id.
      */
-    public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
+    public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host,
+            @Nullable final String reason) {
         notifyDelete(Collections.singleton(info));
         if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
             // Deleting an app widget ID is a void call but writes to disk before returning
             // to the caller...
             enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId));
         }
-        deleteItemFromDatabase(info);
+        deleteItemFromDatabase(info, reason);
     }
 
     private void notifyDelete(Collection<? extends ItemInfo> items) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 239dd45..489bc38 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -311,7 +311,9 @@
 
             bindUpdatedWorkspaceItems(updatedWorkspaceItems);
             if (!removedShortcuts.isEmpty()) {
-                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
+                deleteAndBindComponentsRemoved(
+                        ItemInfoMatcher.ofItemIds(removedShortcuts),
+                        "removed because the target component is invalid");
             }
 
             if (!widgets.isEmpty()) {
@@ -340,7 +342,8 @@
             Predicate<ItemInfo> removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
                     .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
                     .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
-            deleteAndBindComponentsRemoved(removeMatch);
+            deleteAndBindComponentsRemoved(removeMatch,
+                    "removed because the corresponding package or component is removed");
 
             // Remove any queued items from the install queue
             ItemInstallQueue.INSTANCE.get(context)
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 4296d32..1026e0b 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -108,7 +108,8 @@
                 deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(
                         nonPinnedIds.stream()
                                 .map(id -> new ShortcutKey(mPackageName, mUser, id))
-                                .collect(Collectors.toSet())));
+                                .collect(Collectors.toSet())),
+                        "removed because the shortcut is no longer available in shortcut service");
             }
         }
 
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 5048e13..1565b19 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -96,7 +96,9 @@
         }
         bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!removedKeys.isEmpty()) {
-            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
+            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys),
+                    "removed during unlock because it's no longer available"
+                            + " (possibly due to clear data)");
         }
 
         // Remove shortcut id map for that user
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 0c39632..49d27b7 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -213,6 +213,14 @@
             }
 
             if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+                if (getPreferenceScreen().getTitle().equals(
+                        getResources().getString(R.string.search_pref_screen_title))){
+                    DeviceProfile mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(
+                            getContext()).getDeviceProfile(getContext());
+                    getPreferenceScreen().setTitle(mDeviceProfile.isTablet ?
+                            R.string.search_pref_screen_title_tablet
+                            : R.string.search_pref_screen_title);
+                }
                 getActivity().setTitle(getPreferenceScreen().getTitle());
             }
         }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 9f50ff9..2aa9dde 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -335,13 +335,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 // Change the internal state only when the transition actually starts
-                onStateTransitionStart(mCancelled ? mCurrentStableState : state);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                super.onAnimationCancel(animation);
-                mState = mCurrentStableState;
+                onStateTransitionStart(state);
             }
 
             @Override
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index c4d2bc4..e95a787 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -176,7 +176,8 @@
                 .setNeutralButton(R.string.abandoned_clean_this,
                         (d, i) -> launcher.getWorkspace()
                                 .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
-                                        Collections.singleton(packageName), user)))
+                                        Collections.singleton(packageName), user),
+                                        "user explicitly removes the promise app icon"))
                 .create().show();
     }
 
@@ -259,7 +260,8 @@
                             // Remove the icon if launcher is successfully initialized
                             launcher.getWorkspace().persistRemoveItemsByMatcher(ItemInfoMatcher
                                     .ofShortcutKeys(Collections.singleton(ShortcutKey
-                                            .fromItemInfo(shortcut))));
+                                            .fromItemInfo(shortcut))),
+                                    "user explicitly removes disabled shortcut");
                         })
                         .create()
                         .show();
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 4c001fd..f553fb4 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -40,6 +40,7 @@
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -553,8 +554,14 @@
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         if (Utilities.ATLEAST_Q) {
             Insets gestureInsets = insets.getMandatorySystemGestureInsets();
+            int gestureInsetBottom = gestureInsets.bottom;
+            DeviceProfile dp = mActivity.getDeviceProfile();
+            if (dp.isTaskbarPresent) {
+                // Ignore taskbar gesture insets to avoid interfering with TouchControllers.
+                gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarSize);
+            }
             mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
-                    gestureInsets.right, gestureInsets.bottom);
+                    gestureInsets.right, gestureInsetBottom);
         }
         return super.dispatchApplyWindowInsets(insets);
     }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 53c772f..0865152 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -457,7 +457,8 @@
         }
         // Remove and rebind the current widget (which was inflated in the wrong
         // orientation), but don't delete it from the database
-        mLauncher.removeItem(this, info, false  /* deleteFromDb */);
+        mLauncher.removeItem(this, info, false  /* deleteFromDb */,
+                "widget removed because of configuration change");
         mLauncher.bindAppWidget(info);
     }
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a3b05f6..8a97c6b 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -48,7 +48,6 @@
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -70,9 +69,6 @@
     private static final String STORE_APP_NAME = "Play Store";
     private static final String GMAIL_APP_NAME = "Gmail";
 
-    @Rule
-    public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -380,7 +376,6 @@
 
     @Test
     @PortraitLandscape
-    @ScreenRecord
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
         // on tablets or phones due to difference in resolution.
@@ -397,6 +392,15 @@
                 workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
         assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
                 workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
+
+        final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
+        folderIcon = mapIcon.dragToIcon(folderIcon);
+        folder = folderIcon.open();
+        folder.getAppIcon(MAPS_APP_NAME);
+        workspace = folder.close();
+
+        assertNull(MAPS_APP_NAME + " should be moved to a folder.",
+                workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME));
     }
 
     @Test
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 954af3d..eb7f05b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -394,6 +394,7 @@
                 launcher,
                 launchable,
                 destSupplier,
+                /* isDecelerating= */ false,
                 () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
                 /* expectDropEvents= */ null);
     }
@@ -404,6 +405,17 @@
             Supplier<Point> dest,
             Runnable expectLongClickEvents,
             @Nullable Runnable expectDropEvents) {
+        dragIconToWorkspace(launcher, launchable, dest, /* isDecelerating */ true,
+                expectLongClickEvents, expectDropEvents);
+    }
+
+    static void dragIconToWorkspace(
+            LauncherInstrumentation launcher,
+            Launchable launchable,
+            Supplier<Point> dest,
+            boolean isDecelerating,
+            Runnable expectLongClickEvents,
+            @Nullable Runnable expectDropEvents) {
         try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
                 "want to drag icon to workspace")) {
             final long downTime = SystemClock.uptimeMillis();
@@ -430,7 +442,7 @@
 
             // targetDest.x is now between 0 and displayX so we found the target page,
             // we just have to put move the icon to the destination and drop it
-            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
+            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
                     downTime, SystemClock.uptimeMillis(), false,
                     LauncherInstrumentation.GestureScope.INSIDE);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);