Merge "Adding one more automatic flake classifier" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index a099ada..66ccae0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -144,7 +144,7 @@
 LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
@@ -213,7 +213,7 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 7269b41..7690b9d 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -22,7 +22,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
@@ -59,7 +59,7 @@
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
      */
-    public List<ComponentWithLabel> update(LauncherAppState app,
+    public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
             @Nullable PackageUserKey packageUser) {
         return Collections.emptyList();
     }
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index bce4e0f..31a923e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -350,6 +350,17 @@
                 iconDpi);
     }
 
+    /**
+     * Badges the provided source with the badge info
+     */
+    public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) {
+        Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+            getShadowGenerator().recreateIcon(source, c);
+            badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon));
+        });
+        return BitmapInfo.of(icon, badgeInfo.color);
+    }
+
     private int extractColor(Bitmap bitmap) {
         return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
     }
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index e807791..4c634cb 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -281,7 +281,8 @@
 
         ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
                 componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
-        addIconToDB(values, componentName, info, userSerial);
+        addIconToDB(values, componentName, info, userSerial,
+                cachingLogic.getLastUpdatedTime(object, info));
     }
 
     /**
@@ -289,10 +290,10 @@
      * @param values {@link ContentValues} containing icon & title
      */
     private void addIconToDB(ContentValues values, ComponentName key,
-            PackageInfo info, long userSerial) {
+            PackageInfo info, long userSerial, long lastUpdateTime) {
         values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
         values.put(IconDB.COLUMN_USER, userSerial);
-        values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
+        values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime);
         values.put(IconDB.COLUMN_VERSION, info.versionCode);
         mIconDb.insertOrReplace(values);
     }
@@ -362,7 +363,8 @@
                 }
                 if (object != null) {
                     entry.title = cachingLogic.getLabel(object);
-                    entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
+                    entry.contentDescription = mPackageManager.getUserBadgedLabel(
+                            cachingLogic.getDescription(object, entry.title), user);
                 }
             }
         }
@@ -449,7 +451,8 @@
                     // package updates.
                     ContentValues values = newContentValues(
                             iconInfo, entry.title.toString(), packageName, null);
-                    addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
+                    addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user),
+                            info.lastUpdateTime);
 
                 } catch (NameNotFoundException e) {
                     if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index a89ede7..c12e9dc 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -34,6 +34,10 @@
 
     CharSequence getLabel(T object);
 
+    default CharSequence getDescription(T object, CharSequence fallback) {
+        return fallback;
+    }
+
     @NonNull
     BitmapInfo loadIcon(Context context, T object);
 
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
index bcdbce5..aec1cdd 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
@@ -24,6 +24,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 
@@ -61,7 +62,7 @@
     private final HashMap<String, PackageInfo> mPkgInfoMap;
     private final BaseIconCache mIconCache;
 
-    private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
+    private final ArrayMap<UserHandle, Set<String>> mPackagesToIgnore = new ArrayMap<>();
 
     private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
     private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
@@ -77,8 +78,16 @@
         createPackageInfoMap();
     }
 
-    public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
-        mPackagesToIgnore.put(userHandle, packages);
+    /**
+     * Sets a package to ignore for processing
+     */
+    public void addPackagesToIgnore(UserHandle userHandle, String packageName) {
+        Set<String> packages = mPackagesToIgnore.get(userHandle);
+        if (packages == null) {
+            packages = new HashSet<>();
+            mPackagesToIgnore.put(userHandle, packages);
+        }
+        packages.add(packageName);
     }
 
     private void createPackageInfoMap() {
@@ -181,6 +190,7 @@
                     }
                     continue;
                 }
+
                 if (app == null) {
                     if (mFilterMode == MODE_SET_INVALID_ITEMS) {
                         mIconCache.remove(component, user);
@@ -263,6 +273,7 @@
                 T app = mAppsToUpdate.pop();
                 String pkg = mCachingLogic.getComponent(app).getPackageName();
                 PackageInfo info = mPkgInfoMap.get(pkg);
+
                 mIconCache.addIconToDBAndMemCache(
                         app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
                 mUpdatedPackages.add(pkg);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 76972af..54f58e2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -43,7 +43,6 @@
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.InstantAppResolver;
@@ -170,14 +169,7 @@
         List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
         if (!details.isEmpty()) {
             WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
-            try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
-                si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
-            } catch (Exception e) {
-                if (DEBUG) {
-                    Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
-                }
-                return null;
-            }
+            mIconCache.getShortcutIcon(si, details.get(0));
             return si;
         }
         if (DEBUG) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 109439f..cc6ec69 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
@@ -443,6 +444,20 @@
         mHotseat.invalidate();
     }
 
+    /**
+     * Unpins pinned app when it's converted into a folder
+     */
+    public void folderCreatedFromIcon(ItemInfo info, FolderInfo folderInfo) {
+        AppTarget target = getAppTargetFromItemInfo(info);
+        if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && !isInHotseat(
+                info)) {
+            notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
+        } else if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+                && folderInfo.screenId == Workspace.FIRST_SCREEN_ID && !isInFirstPage(info)) {
+            notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+        }
+    }
+
     @Override
     public void onDragEnd() {
         if (mDragObject == null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index c359423..b87fcf2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -26,11 +26,14 @@
 import android.view.Gravity;
 
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.popup.SystemShortcut;
@@ -169,6 +172,16 @@
     }
 
     @Override
+    public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
+            int screenId, int cellX, int cellY) {
+        FolderIcon fi =  super.addFolder(layout, info, container, screenId, cellX, cellY);
+        if (mHotseatPredictionController != null) {
+            mHotseatPredictionController.folderCreatedFromIcon(info, fi.getFolder().getInfo());
+        }
+        return fi;
+    }
+
+    @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         if (mHotseatPredictionController != null) {
             return Stream.concat(super.getSupportedShortcuts(),
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index bb66ae1..48a2f32 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -39,7 +37,7 @@
     }
 
     protected BackgroundAppState(int id, int logContainer) {
-        super(id, logContainer, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+        super(id, logContainer, STATE_FLAGS);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 9c78af9..a1c8378 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,7 +17,6 @@
 
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
@@ -33,6 +32,8 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.graphics.Rect;
 import android.view.View;
@@ -67,15 +68,22 @@
             | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
 
     public OverviewState(int id) {
-        this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+        this(id, STATE_FLAGS);
     }
 
-    protected OverviewState(int id, int transitionDuration, int stateFlags) {
-        this(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+    protected OverviewState(int id, int stateFlags) {
+        this(id, ContainerType.TASKSWITCHER, stateFlags);
     }
 
-    protected OverviewState(int id, int logContainer, int transitionDuration, int stateFlags) {
-        super(id, logContainer, transitionDuration, stateFlags);
+    protected OverviewState(int id, int logContainer, int stateFlags) {
+        super(id, logContainer, stateFlags);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher launcher) {
+        // In no-button mode, overview comes in all the way from the left, so give it more time.
+        boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(launcher).getMode() == NO_BUTTON;
+        return isNoButtonMode && ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
     }
 
     @Override
@@ -118,7 +126,8 @@
 
     @Override
     public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
-        if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) {
+        if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()
+                && removeShelfFromOverview(launcher)) {
             // Treat the QSB as part of the hotseat so they move together.
             return getHotseatScaleAndTranslation(launcher);
         }
@@ -151,7 +160,7 @@
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
         } else {
-            if (ENABLE_OVERVIEW_ACTIONS.get()) {
+            if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
                 return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
             }
 
@@ -206,7 +215,7 @@
     public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
             AnimatorSetBuilder builder) {
         if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
-            if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
+            if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) {
                 builder.setInterpolator(ANIM_WORKSPACE_SCALE,
                         fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                 builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
@@ -222,6 +231,7 @@
             builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
             builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
             Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
+                    && removeShelfFromOverview(launcher)
                     ? OVERSHOOT_1_2
                     : OVERSHOOT_1_7;
             builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 2ac2d2d..34fc3e4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -109,7 +109,7 @@
             // Normally we compute the duration based on the velocity and distance to the given
             // state, but since the hint state tracks the entire screen without a clear endpoint, we
             // need to manually set the duration to a reasonable value.
-            animator.setDuration(HINT_STATE.transitionDuration);
+            animator.setDuration(HINT_STATE.getTransitionDuration(mLauncher));
         }
     }
 
@@ -173,7 +173,7 @@
                 // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
                 stateManager.cancelAnimation();
                 AnimatorSetBuilder builder = new AnimatorSetBuilder();
-                long duration = OVERVIEW.transitionDuration;
+                long duration = OVERVIEW.getTransitionDuration(mLauncher);
                 AnimatorSet anim = stateManager.createAtomicAnimation(
                         stateManager.getState(), NORMAL, builder,
                         ATOMIC_OVERVIEW_PEEK_COMPONENT, duration);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 8b5283e..f0516ac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -75,7 +75,6 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SharedApiCompat;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
 import com.android.quickstep.views.LiveTileOverlay;
@@ -973,7 +972,7 @@
         }
         mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
 
-        if (UNSTABLE_SPRINGS.get()) {
+        if (QUICKSTEP_SPRINGS.get()) {
             mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
         }
         mLauncherTransitionController.getAnimationPlayer().start();
@@ -1142,8 +1141,7 @@
         final int runningTaskId = mGestureState.getRunningTaskId();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationController != null) {
-                SharedApiCompat.setWillFinishToHome(mRecentsAnimationController.getController(),
-                        true /* willFinishToHome */);
+                mRecentsAnimationController.getController().setWillFinishToHome(true);
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
                     mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c4733bd..fc50660 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -74,6 +74,10 @@
 
     @BinderThread
     public void onOverviewShown(boolean triggeredFromAltTab) {
+        if (triggeredFromAltTab) {
+            ActivityManagerWrapper.getInstance()
+                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        }
         MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 2f73fc1..8ae4f06 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -2,6 +2,7 @@
 
 import android.view.MotionEvent;
 
+import com.android.launcher3.testing.TestLogging;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -34,6 +35,7 @@
 
     protected void setActive(MotionEvent ev) {
         mState = STATE_ACTIVE;
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitor.pilferPointers();
 
         // Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 5a34520..d01e1a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
@@ -202,6 +203,7 @@
 
     private void startRecentsTransition() {
         mThresholdCrossed = true;
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         Intent intent = new Intent(Intent.ACTION_MAIN)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index bf2128d..1b0e05a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -44,6 +44,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.R;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface;
@@ -302,6 +303,7 @@
         if (mInteractionHandler == null) {
             return;
         }
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index c19754f..32a67ca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -25,6 +25,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
@@ -106,6 +107,7 @@
                 ActiveGestureLog.INSTANCE.addLog("startQuickstep");
             }
             if (mInputMonitor != null) {
+                TestLogging.recordEvent("pilferPointers");
                 mInputMonitor.pilferPointers();
             }
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index ca15ca1..6bfc3fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.GestureState;
@@ -63,6 +64,7 @@
 
     private void onInterceptTouch() {
         if (mInputMonitor != null) {
+            TestLogging.recordEvent("pilferPointers");
             mInputMonitor.pilferPointers();
         }
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 4a39e73..9e29238 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -224,7 +224,6 @@
             surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
                     cornerRadius / scale);
         }
-        applySurfaceParams(params.syncTransactionApplier, surfaceParams);
         return surfaceParams;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index c1ea533..aaba308 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -346,7 +346,7 @@
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mTaskTopMargin = getResources()
                 .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-        mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
+        mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(context);
         mSquaredTouchSlop = squaredTouchSlop(context);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 3bc1509..49f667e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -20,7 +20,6 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -38,11 +37,11 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -102,7 +101,7 @@
     private float mSaturation = 1f;
 
     private boolean mOverlayEnabled;
-    private boolean mRotated;
+    private boolean mIsOrientationChanged;
     private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
 
     public TaskThumbnailView(Context context) {
@@ -323,7 +322,7 @@
 
     private void updateOverlay() {
         // The overlay doesn't really work when the screenshot is rotated, so don't add it.
-        if (mOverlayEnabled && !mRotated && mBitmapShader != null && mThumbnailData != null) {
+        if (mOverlayEnabled && !mIsOrientationChanged && mBitmapShader != null && mThumbnailData != null) {
             mOverlay.initOverlay(mTask, mThumbnailData, mMatrix);
         } else {
             mOverlay.reset();
@@ -346,6 +345,7 @@
 
     private void updateThumbnailMatrix() {
         boolean isRotated = false;
+        boolean isOrientationDifferent = false;
         mClipBottom = -1;
         if (mBitmapShader != null && mThumbnailData != null) {
             float scale = mThumbnailData.scale;
@@ -356,51 +356,40 @@
                     (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
 
             final float thumbnailScale;
-            final DeviceProfile profile = mActivity.getDeviceProfile();
-
+            int thumbnailRotation = mThumbnailData.rotation;
+            int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
+            int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
+            // Landscape vs portrait change
+            boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
+                    && mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+            isOrientationDifferent = isOrientationChange(deltaRotate)
+                && windowingModeSupportsRotation;
             if (getMeasuredWidth() == 0) {
                 // If we haven't measured , skip the thumbnail drawing and only draw the background
                 // color
                 thumbnailScale = 0f;
             } else {
-                final Configuration configuration =
-                        getContext().getResources().getConfiguration();
                 // Rotate the screenshot if not in multi-window mode
-                isRotated = configuration.orientation != mThumbnailData.orientation &&
-                        !mActivity.isInMultiWindowMode() &&
-                        mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+                isRotated = deltaRotate > 0 && windowingModeSupportsRotation;
                 // Scale the screenshot to always fit the width of the card.
-                thumbnailScale = isRotated
+
+                thumbnailScale = isOrientationDifferent
                         ? getMeasuredWidth() / thumbnailHeight
                         : getMeasuredWidth() / thumbnailWidth;
             }
 
-            if (isRotated) {
-                int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
-                mMatrix.setRotate(90 * rotationDir);
-                int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
-                int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
-                mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
-                if (rotationDir == -1) {
-                    // Crop the right/bottom side of the screenshot rather than left/top
-                    float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
-                    mClippedInsets.offset(0, excessHeight);
-                }
-                mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
-                // Move the screenshot to the thumbnail window (rotation moved it out).
-                if (rotationDir == 1) {
-                    mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
-                } else {
-                    mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
-                }
-            } else {
-                mClippedInsets.offsetTo(thumbnailInsets.left * scale, thumbnailInsets.top * scale);
+            if (!isRotated) {
+                // No Rotation
+                mClippedInsets.offsetTo(thumbnailInsets.left * scale,
+                    thumbnailInsets.top * scale);
                 mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top);
+            } else {
+                setThumbnailRotation(deltaRotate, thumbnailInsets, scale);
             }
 
             final float widthWithInsets;
             final float heightWithInsets;
-            if (isRotated) {
+            if (isOrientationDifferent) {
                 widthWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
                 heightWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
             } else {
@@ -415,7 +404,7 @@
             mMatrix.postScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mMatrix);
 
-            float bitmapHeight = Math.max((isRotated ? thumbnailWidth : thumbnailHeight)
+            float bitmapHeight = Math.max((isOrientationDifferent ? thumbnailWidth : thumbnailHeight)
                     * thumbnailScale, 0);
             if (Math.round(bitmapHeight) < getMeasuredHeight()) {
                 mClipBottom = bitmapHeight;
@@ -423,7 +412,7 @@
             mPaint.setShader(mBitmapShader);
         }
 
-        mRotated = isRotated;
+        mIsOrientationChanged = isOrientationDifferent;
         invalidate();
 
         // Update can be called from {@link #onSizeChanged} during layout, post handling of overlay
@@ -431,6 +420,51 @@
         post(this::updateOverlay);
     }
 
+    private int getRotationDelta(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+
+    /**
+     * @param deltaRotation the number of 90 degree turns from the current orientation
+     * @return {@code true} if the change in rotation results in a shift from landscape to portrait
+     * or vice versa, {@code false} otherwise
+     */
+    private boolean isOrientationChange(int deltaRotation) {
+        return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
+    }
+
+    private void setThumbnailRotation(int deltaRotate, Rect thumbnailInsets, float scale) {
+        int newLeftInset = 0;
+        int newTopInset = 0;
+        int translateX = 0;
+        int translateY = 0;
+
+        mMatrix.setRotate(90 * deltaRotate);
+        switch (deltaRotate) { /* Counter-clockwise */
+            case Surface.ROTATION_90:
+                newLeftInset = thumbnailInsets.bottom;
+                newTopInset = thumbnailInsets.left;
+                translateX = mThumbnailData.thumbnail.getHeight();
+                break;
+            case Surface.ROTATION_270:
+                newLeftInset = thumbnailInsets.top;
+                newTopInset = thumbnailInsets.right;
+                translateY = mThumbnailData.thumbnail.getWidth();
+                break;
+            case Surface.ROTATION_180:
+                newLeftInset = -thumbnailInsets.top;
+                newTopInset = -thumbnailInsets.left;
+                translateX = mThumbnailData.thumbnail.getWidth();
+                translateY = mThumbnailData.thumbnail.getHeight();
+                break;
+        }
+        mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
+        mMatrix.postTranslate(translateX - mClippedInsets.left,
+                translateY - mClippedInsets.top);
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 58e36bc..294bb7b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -30,7 +31,6 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -221,7 +221,7 @@
         mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
         mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
 
-        mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
+        mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams);
         setOutlineProvider(mOutlineProvider);
     }
 
@@ -230,19 +230,21 @@
         super.onFinishInflate();
         mSnapshotView = findViewById(R.id.snapshot);
         mIconView = findViewById(R.id.icon);
+        final Context context = getContext();
 
         TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
-        thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
+        thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context);
         mSnapshotView.setLayoutParams(thumbnailParams);
 
 
-        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
             mActionsView = mSnapshotView.getTaskOverlay().getActionsView();
             if (mActionsView != null) {
                 TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
                         getResources().getDimensionPixelSize(R.dimen.overview_actions_height),
                         Gravity.BOTTOM);
                 addView(mActionsView, params);
+                mActionsView.setAlpha(0);
             }
         }
     }
@@ -447,7 +449,8 @@
         mIconView.setScaleX(scale);
         mIconView.setScaleY(scale);
 
-        if (mActionsView != null) {
+
+        if (mActionsView != null && isRunningTask()) {
             mActionsView.setAlpha(scale);
         }
 
@@ -523,9 +526,11 @@
     public void onPageScroll(ScrollState scrollState) {
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
+        float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
+                curveInterpolation);
 
         mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
-        setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
+        setCurveScale(curveScaleForCurveInterpolation);
 
         mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
         for (FooterWrapper footer : mFooters) {
@@ -539,6 +544,28 @@
             mMenuView.setScaleX(getScaleX());
             mMenuView.setScaleY(getScaleY());
         }
+
+        // This is not the proper implementation and will be replaced with a proper layout.
+        if (mActionsView != null) {
+            if (mFocusTransitionProgress == 1f) {
+                mActionsView.setAlpha(1 - curveInterpolation / MAX_PAGE_SCRIM_ALPHA);
+            }
+            maintainActionViewPosition(curveScaleForCurveInterpolation);
+        }
+
+    }
+
+    private void maintainActionViewPosition(float curveScaleForCurveInterpolation) {
+        float inverseCurveScaleFactor = curveScaleForCurveInterpolation == 0 ? 0 :
+                (1f / curveScaleForCurveInterpolation);
+        mActionsView.setScaleX(inverseCurveScaleFactor);
+        mActionsView.setScaleY(inverseCurveScaleFactor);
+        mActionsView.setTranslationX(inverseCurveScaleFactor * (-getX()
+                + getRecentsView().getScrollX() + getRecentsView().scrollOffsetLeft()));
+        mActionsView.setTranslationY(
+                (1f - curveScaleForCurveInterpolation) * (mSnapshotView.getHeight()
+                        + mActionsView.getHeight()) / 2f
+                        + inverseCurveScaleFactor * (-getTranslationY()));
     }
 
 
@@ -653,9 +680,10 @@
         private final int mMarginBottom;
         private FullscreenDrawParams mFullscreenParams;
 
-        TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
-            mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-            mMarginBottom = LayoutUtils.thumbnailBottomMargin(res);
+        TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams) {
+            mMarginTop = context.getResources().getDimensionPixelSize(
+                    R.dimen.task_thumbnail_top_margin);
+            mMarginBottom = LayoutUtils.thumbnailBottomMargin(context);
             mFullscreenParams = fullscreenParams;
         }
 
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index ee53386..94ff63b 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
@@ -35,6 +36,7 @@
 
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -187,6 +189,16 @@
     }
 
     @Override
+    protected void setupViews() {
+        super.setupViews();
+
+        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
+            // Overview is above all other launcher elements, including qsb, so move it to the top.
+            getOverviewPanel().bringToFront();
+        }
+    }
+
+    @Override
     protected StateHandler[] createStateHandlers() {
         return new StateHandler[] {
                 getAllAppsController(),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 85a9545..e88a8a4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -40,7 +39,12 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
+        super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher launcher) {
+        return 320;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index d5ce734..99b2a81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -25,7 +25,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.TimeInterpolator;
@@ -277,7 +277,7 @@
     private void handleFirstSwipeToOverview(final ValueAnimator animator,
             final long expectedDuration, final LauncherState targetState, final float velocity,
             final boolean isFling) {
-        if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+        if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
                 && targetState == OVERVIEW) {
             mFinishFastOnSecondTouch = true;
         } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 9817e32..32268a4 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,11 +15,6 @@
  */
 package com.android.quickstep;
 
-import static android.content.Context.MODE_PRIVATE;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
-
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.UserManager;
@@ -27,7 +22,6 @@
 
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.MainProcessInitializer;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
 @SuppressWarnings("unused")
@@ -55,22 +49,7 @@
         super.init(context);
 
         // Elevate GPU priority for Quickstep and Remote animations.
-        ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-
-        // Force disable some feature flags based on the system ui navigation mode.
-        SysUINavigationMode.Mode currMode = SysUINavigationMode.INSTANCE.get(context)
-                .addModeChangeListener(mode -> disableFeatureFlagsForSysuiNavMode(context, mode));
-        disableFeatureFlagsForSysuiNavMode(context, currMode);
-    }
-
-    private void disableFeatureFlagsForSysuiNavMode(Context ctx, SysUINavigationMode.Mode mode) {
-        if (mode == SysUINavigationMode.Mode.TWO_BUTTONS) {
-            ctx.getSharedPreferences(FLAGS_PREF_NAME, MODE_PRIVATE)
-                    .edit()
-                    .putBoolean(ENABLE_OVERVIEW_ACTIONS.key, false)
-                    .apply();
-
-            FeatureFlags.initialize(ctx);
-        }
+        ThreadedRendererCompat.setContextPriority(
+                ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 388b323..375e589 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -122,6 +122,12 @@
         }
     }
 
+    /** @return Whether we can remove the shelf from overview. */
+    public static boolean removeShelfFromOverview(Context context) {
+        // The shelf is core to the two-button mode model, so we need to continue supporting it.
+        return getMode(context) != Mode.TWO_BUTTONS;
+    }
+
     public interface NavigationModeChangeListener {
 
         void onNavigationModeChanged(Mode newMode);
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3c6537a..101bb07 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -29,7 +29,6 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.quickstep.util.SharedApiCompat;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
 /**
@@ -278,7 +277,7 @@
             mLastShelfVisible = visible;
             mLastShelfHeight = shelfHeight;
             try {
-                SharedApiCompat.setShelfHeight(mSystemUiProxy, visible, shelfHeight);
+                mSystemUiProxy.setShelfHeight(visible, shelfHeight);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setShelfHeight visible: " + visible
                         + " height: " + shelfHeight, e);
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index b249f48..9cf55b3 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.util;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -59,7 +60,7 @@
         } else {
             Resources res = context.getResources();
 
-            if (ENABLE_OVERVIEW_ACTIONS.get()) {
+            if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
                 //TODO: this needs to account for the swipe gesture height and accessibility
                 // UI when shown.
                 extraSpace = 0;
@@ -112,7 +113,7 @@
             final int paddingResId;
             if (dp.isVerticalBarLayout()) {
                 paddingResId = R.dimen.landscape_task_card_horz_space;
-            } else if (ENABLE_OVERVIEW_ACTIONS.get()) {
+            } else if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
                 paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
             } else {
                 paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -121,7 +122,7 @@
         }
 
         float topIconMargin =   res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float bottomMargin = thumbnailBottomMargin(res);
+        float bottomMargin = thumbnailBottomMargin(context);
         float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
 
         // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
@@ -147,7 +148,7 @@
 
     public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
         // Track the bottom of the window.
-        if (ENABLE_OVERVIEW_ACTIONS.get()) {
+        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
             Rect taskSize = new Rect();
             calculateLauncherTaskSize(context, dp, taskSize);
             return (dp.heightPx - taskSize.height()) / 2;
@@ -162,9 +163,9 @@
      * Get the margin that the task thumbnail view should use.
      * @return the margin in pixels.
      */
-    public static int thumbnailBottomMargin(Resources resources) {
-        if (ENABLE_OVERVIEW_ACTIONS.get()) {
-            return resources.getDimensionPixelSize(R.dimen.overview_actions_height);
+    public static int thumbnailBottomMargin(Context context) {
+        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
+            return context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height);
         } else {
             return 0;
         }
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 7ed1e21..1ce3549 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -160,7 +160,8 @@
             Context context = getContext();
             if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
                 mDragHandleProgress = 1;
-                if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+                if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
+                        && SysUINavigationMode.removeShelfFromOverview(context)) {
                     // Fade in all apps background quickly to distinguish from swiping from nav bar.
                     mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
                     mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index bd2c539..a726052 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -147,7 +147,7 @@
     @Test
     @Ignore // b/143488140
     public void goToOverviewFromApp() {
-        startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
 
         mLauncher.getBackground().switchToOverview();
     }
@@ -182,8 +182,8 @@
     @Test
     @Ignore // b/143488140
     public void testOverview() {
-        startAppFastAndWaitForRecentTask(getAppPackageName());
-        startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        startAppFast(getAppPackageName());
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
         startTestActivity(2);
         Wait.atMost("Expected three apps in the task list",
                 () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
@@ -236,25 +236,4 @@
     private int getTaskCount(RecentsActivity recents) {
         return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
-
-    /**
-     * Workaround for b/141580748, there was an issue where the recent task is only updated when the
-     * activity starting the task is resumed.  In this case, we should wait until the task is in
-     * the recents task list before continuing.
-     */
-    private void startAppFastAndWaitForRecentTask(String packageName) {
-        startAppFast(packageName);
-        Wait.atMost("Expected app in task list",
-                () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT,
-                mLauncher);
-    }
-
-    private boolean containsRecentTaskWithPackage(String packageName) {
-        for (ComponentName cn : mLauncher.getRecentTasks()) {
-            if (cn.getPackageName().equals(packageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index d782f8d..d2f5d8f 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -273,6 +273,8 @@
     @PortraitLandscape
     // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
     @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+    // b/143285809
+    @Ignore
     public void testQuickSwitchFromApp() throws Exception {
         startTestActivity(2);
         startTestActivity(3);
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 0da51d9..a41fb9a 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -57,7 +57,6 @@
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
                 android:text="@string/all_apps_personal_tab"
-                android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
                 android:textSize="14sp" />
 
@@ -68,7 +67,6 @@
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
                 android:text="@string/all_apps_work_tab"
-                android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
                 android:textSize="14sp" />
         </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 893d796..923352e 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -46,9 +46,9 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical"
+            style="@style/TextHeadline"
             android:layout_weight="1"
             android:background="@android:color/transparent"
-            android:fontFamily="sans-serif-condensed"
             android:textStyle="bold"
             android:gravity="center_horizontal"
             android:hint="@string/folder_hint_text"
@@ -58,7 +58,7 @@
             android:singleLine="true"
             android:textColor="?attr/folderTextColor"
             android:textColorHighlight="?android:attr/colorControlHighlight"
-            android:textColorHint="?attr/folderTextColor"
+            android:textColorHint="?attr/folderHintColor"
             android:textSize="@dimen/folder_label_text_size" />
 
         <com.android.launcher3.pageindicators.PageIndicatorDots
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index de17eb7..aef878b 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -40,6 +40,7 @@
     <attr name="folderIconRadius" format="float" />
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
+    <attr name="folderHintColor" format="color" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
@@ -115,6 +116,7 @@
         <attr name="numFolderColumns" format="integer" />
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
+        <attr name="dbFile" format="string" />
         <attr name="defaultLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
     </declare-styleable>
diff --git a/res/values/config.xml b/res/values/config.xml
index 0945642..ef34dcd 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -108,6 +108,7 @@
     <item type="id" name="action_shortcuts_and_notifications"/>
     <item type="id" name="action_dismiss_notification" />
     <item type="id" name="action_remote_action_shortcut" />
+    <item type="id" name="action_dismiss_prediction" />
 
     <!-- QSB IDs. DO not change -->
     <item type="id" name="search_container_workspace" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bfa92f7..bde3c31 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -142,7 +142,7 @@
     <string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
 
     <!-- Default folder title -->
-    <string name="folder_hint_text">Unnamed Folder</string>
+    <string name="folder_hint_text">Tap to edit</string>
 
     <!-- Accessibility -->
     <!-- The format string for when an app is temporarily disabled. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 35ae49c..1174a2f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -48,6 +48,7 @@
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">#FF212121</item>
+        <item name="folderHintColor">#FF616161</item>
         <item name="loadingIconColor">#CCFFFFFF</item>
 
         <item name="android:windowTranslucentStatus">false</item>
@@ -96,6 +97,7 @@
         <item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
         <item name="folderIconBorderColor">#FF80868B</item>
         <item name="folderTextColor">@android:color/white</item>
+        <item name="folderHintColor">#FFCCCCCC</item>
         <item name="isMainColorDark">true</item>
         <item name="loadingIconColor">#99FFFFFF</item>
     </style>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 82547d5..1c99dfc 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -24,6 +24,7 @@
         launcher:numFolderRows="2"
         launcher:numFolderColumns="3"
         launcher:numHotseatIcons="3"
+        launcher:dbFile="launcher_3_by_3.db"
         launcher:defaultLayoutId="@xml/default_workspace_3x3" >
 
         <display-option
@@ -51,6 +52,7 @@
         launcher:numFolderRows="3"
         launcher:numFolderColumns="4"
         launcher:numHotseatIcons="4"
+        launcher:dbFile="launcher_4_by_4.db"
         launcher:defaultLayoutId="@xml/default_workspace_4x4" >
 
         <display-option
@@ -102,6 +104,7 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:numHotseatIcons="5"
+        launcher:dbFile="launcher.db"
         launcher:defaultLayoutId="@xml/default_workspace_5x5" >
 
         <display-option
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index 6059981..7c7e73c 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -52,7 +52,7 @@
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_TEST_PACKAGE := Launcher3
-LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
+LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
 
 LOCAL_ROBOTEST_TIMEOUT := 36000
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 7072adf..50f9494 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -134,7 +134,7 @@
                 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
             }}, 2, OLD_WORK_PROFILE_ID);
         // simulates the creation of backup upon restore
-        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
+        new GridBackupTable(RuntimeEnvironment.application, mDb, mDb, mIdp.numHotseatIcons,
                 mIdp.numColumns, mIdp.numRows).doBackup(
                         MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
         // reset favorites table
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
index f46b849..36fd96b 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -63,7 +63,7 @@
 
     @Test
     public void backupTableCreated() {
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 4, 4, 4);
         assertFalse(backupTable.backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
@@ -75,14 +75,14 @@
 
     @Test
     public void backupTableRestored() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         // Delete entries
         mDb.delete(TABLE_NAME, null, null);
         assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
 
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 3, 3, 3);
         assertTrue(backupTable.backupOrRestoreAsNeeded());
 
         // Items have been restored
@@ -96,7 +96,7 @@
 
     @Test
     public void backupTableRemovedOnAdd() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -107,7 +107,7 @@
 
     @Test
     public void backupTableRemovedOnDelete() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -118,7 +118,7 @@
 
     @Test
     public void backupTableRetainedOnUpdate() {
-        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 3eb02b3..a5142d8 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
 
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -44,7 +43,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.LauncherIcons;
@@ -61,7 +59,6 @@
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -491,13 +488,8 @@
                 return Pair.create(si, null);
             } else if (shortcutInfo != null) {
                 WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
-                if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
-                    fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
-                } else {
-                    LauncherIcons li = LauncherIcons.obtain(mContext);
-                    itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
-                    li.recycle();
-                }
+                LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
+                        itemInfo, shortcutInfo);
                 return Pair.create(itemInfo, shortcutInfo);
             } else if (providerInfo != null) {
                 LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index a807e4f..857db8e 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -119,6 +119,7 @@
      */
     public int numAllAppsColumns;
 
+    public String dbFile;
     public int defaultLayoutId;
     int demoModeLayoutId;
 
@@ -146,6 +147,7 @@
         iconTextSize = p.iconTextSize;
         numHotseatIcons = p.numHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
+        dbFile = p.dbFile;
         defaultLayoutId = p.defaultLayoutId;
         demoModeLayoutId = p.demoModeLayoutId;
         mExtraAttrs = p.mExtraAttrs;
@@ -292,6 +294,7 @@
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
+        dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
         numFolderRows = closestProfile.numFolderRows;
@@ -559,6 +562,7 @@
 
         private final int numHotseatIcons;
 
+        private final String dbFile;
         private final int defaultLayoutId;
         private final int demoModeLayoutId;
 
@@ -571,6 +575,7 @@
             numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
             numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
 
+            dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
             defaultLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 3f723d1..c99465c 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -214,4 +214,11 @@
         return id;
     }
 
+    /**
+     * Returns if an Item is a predicted item
+     */
+    public boolean isPredictedItem() {
+        return container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
+                || container == LauncherSettings.Favorites.CONTAINER_PREDICTION;
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 445ebc0..d250658 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -32,7 +32,6 @@
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
-import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
@@ -1139,7 +1138,7 @@
     /**
      * Finds all the views we need and configure them properly.
      */
-    private void setupViews() {
+    protected void setupViews() {
         mDragLayer = findViewById(R.id.drag_layer);
         mFocusHandler = mDragLayer.getFocusIndicatorHelper();
         mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -1175,11 +1174,6 @@
         mDropTargetBar.setup(mDragController);
 
         mAllAppsController.setupViews(mAppsView);
-
-        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
-            // Overview is above all other launcher elements, including qsb, so move it to the top.
-            mOverviewPanel.bringToFront();
-        }
     }
 
     /**
@@ -1721,8 +1715,11 @@
         }
     }
 
-    FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
-            int cellY) {
+    /**
+     * Creates and adds new folder to CellLayout
+     */
+    public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
+            final int screenId, int cellX, int cellY) {
         final FolderInfo folderInfo = new FolderInfo();
         folderInfo.title = "";
 
@@ -1730,7 +1727,8 @@
         getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
 
         // Create the view
-        FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout, folderInfo);
+        FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout,
+                folderInfo);
         mWorkspace.addInScreen(newFolder, folderInfo);
         // Force measure the new folder icon
         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
@@ -2691,7 +2689,7 @@
     }
 
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
+        return Stream.of(APP_INFO, WIDGETS, INSTALL);
     }
 
     public static Launcher getLauncher(Context context) {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index cdfd257..32e9c14 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-
 import android.graphics.drawable.Drawable;
 import android.util.FloatProperty;
 import android.util.Property;
@@ -29,11 +27,7 @@
      * Durations for various state animations. These are not defined in resources to allow
      * easier access from static classes and enums
      */
-    public static final int ALL_APPS_TRANSITION_MS = 320;
-    public static final int OVERVIEW_TRANSITION_MS = ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
-    public static final int SPRING_LOADED_TRANSITION_MS = 150;
     public static final int SPRING_LOADED_EXIT_DELAY = 500;
-    public static final int HINT_TRANSITION_MS = 80;
 
     // The progress of an animation to all apps must be at least this far along to snap to all apps.
     public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 228c07e..56cce78 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -7,10 +7,12 @@
 import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.UserHandle;
 
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.icons.IconCache;
 
 /**
  * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -19,7 +21,7 @@
  * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
  */
 public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
-        implements ComponentWithLabel {
+        implements ComponentWithLabelAndIcon {
 
     public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
 
@@ -111,4 +113,9 @@
     public final UserHandle getUser() {
         return getProfile();
     }
+
+    @Override
+    public Drawable getFullResIcon(IconCache cache) {
+        return cache.getFullResIcon(provider.getPackageName(), icon);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 9c4646b..25afb55 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -15,8 +15,13 @@
     private static final String XML = ".xml";
 
     public static final String LAUNCHER_DB = "launcher.db";
+    public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
+    public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
+    public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
+    public static final String BACKUP_DB = "backup.db";
     public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
-    public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs";
+    public static final String MANAGED_USER_PREFERENCES_KEY =
+            "com.android.launcher3.managedusers.prefs";
     // This preference file is not backed up to cloud.
     public static final String DEVICE_PREFERENCES_KEY = "com.android.launcher3.device.prefs";
 
@@ -25,6 +30,10 @@
 
     public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
             LAUNCHER_DB,
+            LAUNCHER_4_BY_4_DB,
+            LAUNCHER_3_BY_3_DB,
+            LAUNCHER_2_BY_2_DB,
+            BACKUP_DB,
             SHARED_PREFERENCES_KEY + XML,
             WIDGET_PREVIEWS_DB,
             MANAGED_USER_PREFERENCES_KEY + XML,
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 04a7ecd..efe85c7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -37,7 +37,6 @@
 
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.AllAppsList;
@@ -573,9 +572,7 @@
     public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
         updateAndBindWorkspaceItem(() -> {
             si.updateFromDeepShortcutInfo(info, mApp.getContext());
-            LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
-            si.bitmap = li.createShortcutIcon(info);
-            li.recycle();
+            mApp.getIconCache().getShortcutIcon(si, info);
             return si;
         });
     }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b0ab35c..5544240 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -157,6 +158,17 @@
         }
     }
 
+    private synchronized boolean updateCurrentOpenHelper() {
+        final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
+        if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
+            return false;
+        }
+
+        mOpenHelper.close();
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
@@ -210,7 +222,7 @@
         addModifiedTime(initialValues);
         final int rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
         if (rowId < 0) return null;
-        mOpenHelper.onAddOrDeleteOp(db);
+        onAddOrDeleteOp(db);
 
         uri = ContentUris.withAppendedId(uri, rowId);
         reloadLauncherIfExternal();
@@ -268,7 +280,7 @@
                     return 0;
                 }
             }
-            mOpenHelper.onAddOrDeleteOp(db);
+            onAddOrDeleteOp(db);
             t.commit();
         }
 
@@ -294,7 +306,7 @@
                         results[i].count != null && results[i].count > 0;
             }
             if (isAddOrDelete) {
-                mOpenHelper.onAddOrDeleteOp(t.getDb());
+                onAddOrDeleteOp(t.getDb());
             }
 
             t.commit();
@@ -316,7 +328,7 @@
         }
         int count = db.delete(args.table, args.where, args.args);
         if (count > 0) {
-            mOpenHelper.onAddOrDeleteOp(db);
+            onAddOrDeleteOp(db);
             reloadLauncherIfExternal();
         }
         return count;
@@ -360,12 +372,14 @@
             }
             case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
                 Bundle result = new Bundle();
-                result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
+                result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
+                        mOpenHelper.generateNewItemId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
                 Bundle result = new Bundle();
-                result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
+                result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
+                        mOpenHelper.generateNewScreenId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -387,8 +401,12 @@
                 return result;
             }
             case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
-                mOpenHelper.mBackupTableExists =
-                        tableExists(mOpenHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+                // TODO(pinyaoting): Update the behavior here.
+                if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
+                    mOpenHelper.mBackupTableExists =
+                            tableExists(mOpenHelper.getReadableDatabase(),
+                                    Favorites.BACKUP_TABLE_NAME);
+                }
                 return null;
             }
             case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
@@ -399,10 +417,26 @@
                         TOKEN_RESTORE_BACKUP_TABLE, RESTORE_BACKUP_TABLE_DELAY);
                 return null;
             }
+            case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
+                if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
+                    Bundle result = new Bundle();
+                    result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                            updateCurrentOpenHelper());
+                    return result;
+                }
+            }
         }
         return null;
     }
 
+    private void onAddOrDeleteOp(SQLiteDatabase db) {
+        if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
+            // TODO(pingyaoting): Implement the behavior here.
+        } else {
+            mOpenHelper.onAddOrDeleteOp(db);
+        }
+    }
+
     /**
      * Deletes any empty folder from the DB.
      * @return Ids of deleted folders.
@@ -551,14 +585,16 @@
     /**
      * The class is subclassed in tests to create an in-memory db.
      */
-    public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
+    public static class DatabaseHelper extends NoLocaleSQLiteHelper implements
+            LayoutParserCallback {
         private final Context mContext;
         private int mMaxItemId = -1;
         private int mMaxScreenId = -1;
         private boolean mBackupTableExists;
 
         DatabaseHelper(Context context) {
-            this(context, LauncherFiles.LAUNCHER_DB);
+            this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
+                    context).dbFile : LauncherFiles.LAUNCHER_DB);
             // Table creation sometimes fails silently, which leads to a crash loop.
             // This way, we will try to create a table every time after crash, so the device
             // would eventually be able to recover.
@@ -567,7 +603,10 @@
                 // This operation is a no-op if the table already exists.
                 addFavoritesTable(getWritableDatabase(), true);
             }
-            mBackupTableExists = tableExists(getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+            if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
+                mBackupTableExists = tableExists(getReadableDatabase(),
+                        Favorites.BACKUP_TABLE_NAME);
+            }
 
             initIds();
         }
@@ -575,8 +614,8 @@
         /**
          * Constructor used in tests and for restore.
          */
-        public DatabaseHelper(Context context, String tableName) {
-            super(context, tableName, SCHEMA_VERSION);
+        public DatabaseHelper(Context context, String dbName) {
+            super(context, dbName, SCHEMA_VERSION);
             mContext = context;
         }
 
@@ -606,7 +645,7 @@
         }
 
         protected void onAddOrDeleteOp(SQLiteDatabase db) {
-            if (mBackupTableExists) {
+            if (!MULTI_DB_GRID_MIRATION_ALGO.get() && mBackupTableExists) {
                 dropTable(db, Favorites.BACKUP_TABLE_NAME);
                 mBackupTableExists = false;
             }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 4c5c61c..49831f6 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -302,6 +302,8 @@
 
         public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table";
 
+        public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
+
         public static final String EXTRA_VALUE = "value";
 
         public static Bundle call(ContentResolver cr, String method) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8b80cba..16be391 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -41,8 +41,10 @@
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
+import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.states.HintState;
 import com.android.launcher3.states.SpringLoadedState;
@@ -56,7 +58,7 @@
 /**
  * Base state for various states used for the Launcher
  */
-public class LauncherState {
+public abstract class LauncherState {
 
 
     /**
@@ -97,9 +99,15 @@
      * TODO: Create a separate class for NORMAL state.
      */
     public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
-            ContainerType.WORKSPACE, 0,
+            ContainerType.WORKSPACE,
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
-            FLAG_HAS_SYS_UI_SCRIM);
+                    FLAG_HAS_SYS_UI_SCRIM) {
+        @Override
+        public int getTransitionDuration(Launcher launcher) {
+            // Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
+            return 0;
+        }
+    };
 
     /**
      * Various Launcher states arranged in the increasing order of UI layers
@@ -147,8 +155,6 @@
      */
     public final boolean hasWorkspacePageBackground;
 
-    public final int transitionDuration;
-
     /**
      * True if the state allows workspace icons to be dragged.
      */
@@ -178,9 +184,8 @@
 
     public final boolean hasSysUiScrim;
 
-    public LauncherState(int id, int containerType, int transitionDuration, int flags) {
+    public LauncherState(int id, int containerType, int flags) {
         this.containerType = containerType;
-        this.transitionDuration = transitionDuration;
 
         this.hasWorkspacePageBackground = (flags & FLAG_PAGE_BACKGROUNDS) != 0;
         this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0;
@@ -203,6 +208,12 @@
         return Arrays.copyOf(sAllStates, sAllStates.length);
     }
 
+    /**
+     * @return How long the animation to this state should take (or from this state to NORMAL).
+     * @param launcher
+     */
+    public abstract int getTransitionDuration(Launcher launcher);
+
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         return new ScaleAndTranslation(1, 0, 0);
     }
@@ -328,8 +339,13 @@
                 hotseat.setScaleX(0.92f);
                 hotseat.setScaleY(0.92f);
                 if (ENABLE_OVERVIEW_ACTIONS.get()) {
-                    launcher.getAppsView().setScaleX(0.92f);
-                    launcher.getAppsView().setScaleY(0.92f);
+                    AllAppsContainerView qsbContainer = launcher.getAppsView();
+                    View qsb = qsbContainer.getSearchView();
+                    boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
+                    if (!qsbVisible) {
+                        qsbContainer.setScaleX(0.92f);
+                        qsbContainer.setScaleY(0.92f);
+                    }
                 }
             }
         } else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index daf270b..195e69b 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -284,7 +284,9 @@
             Runnable onCompleteRunnable) {
         // Since state NORMAL can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
-        mConfig.duration = state == NORMAL ? fromState.transitionDuration : state.transitionDuration;
+        mConfig.duration = state == NORMAL
+                ? fromState.getTransitionDuration(mLauncher)
+                : state.getTransitionDuration(mLauncher);
 
         AnimatorSetBuilder builder = new AnimatorSetBuilder();
         prepareForAtomicAnimation(fromState, state, builder);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a1888bf..ae4eae9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -245,6 +245,13 @@
         forceFinishScroller(true);
     }
 
+    /**
+     * Returns left offset of a page. This is the gap between pages and prevents overlap.
+     */
+    public int scrollOffsetLeft() {
+        return mInsets.left + getPaddingLeft();
+    }
+
     private void abortScrollerAnimation(boolean resetNextPage) {
         mScroller.abortAnimation();
         // We need to clean up the next page here to avoid computeScrollHelper from
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 3c2ed72..1dbe195 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -6,6 +6,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
 
@@ -29,9 +30,11 @@
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -43,6 +46,7 @@
  * Drop target which provides a secondary option for an item.
  *    For app targets: shows as uninstall
  *    For configurable widgets: shows as setup
+ *    For predicted app icons: don't suggest app
  */
 public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmListener {
 
@@ -81,7 +85,11 @@
             mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
             setDrawable(R.drawable.ic_uninstall_shadow);
             updateText(R.string.uninstall_drop_target_label);
-        } else {
+        } else if (action == DISMISS_PREDICTION) {
+            mHoverColor = Themes.getColorAccent(getContext());
+            setDrawable(R.drawable.ic_block);
+            updateText(R.string.dismiss_prediction_label);
+        } else if (action == RECONFIGURE) {
             mHoverColor = Themes.getColorAccent(getContext());
             setDrawable(R.drawable.ic_setup_shadow);
             updateText(R.string.gadget_setup_text);
@@ -101,8 +109,13 @@
     @Override
     public Target getDropTargetForLogging() {
         Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        t.controlType = mCurrentAccessibilityAction == UNINSTALL ? ControlType.UNINSTALL_TARGET
-                : ControlType.SETTINGS_BUTTON;
+        if (mCurrentAccessibilityAction == UNINSTALL) {
+            t.controlType = ControlType.UNINSTALL_TARGET;
+        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            t.controlType = ControlType.DISMISS_PREDICTION;
+        } else {
+            t.controlType = ControlType.SETTINGS_BUTTON;
+        }
         return t;
     }
 
@@ -119,6 +132,9 @@
                 return true;
             }
             return false;
+        } else if (FeatureFlags.ENABLE_PREDICTION_DISMISS.get() && info.isPredictedItem()) {
+            setupUi(DISMISS_PREDICTION);
+            return true;
         }
 
         setupUi(UNINSTALL);
@@ -229,6 +245,11 @@
             }
             return null;
         }
+        if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(),
+                    info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
+            return null;
+        }
         // else: mCurrentAccessibilityAction == UNINSTALL
 
         ComponentName cn = getUninstallTarget(info);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7f443b6..e0e4cc0 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -584,9 +584,8 @@
                 return new FixedSizeEmptyDrawable(iconSize);
             }
             ShortcutInfo si = (ShortcutInfo) obj;
-            LauncherIcons li = LauncherIcons.obtain(appState.getContext());
-            Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
-            li.recycle();
+            Bitmap badge = LauncherAppState.getInstance(appState.getContext())
+                    .getIconCache().getShortcutInfoBadge(si).icon;
             float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
             float insetFraction = (iconSize - badgeSize) / iconSize;
             return new InsetDrawable(new FastBitmapDrawable(badge),
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index beaafda..b725002 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -16,12 +16,11 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
@@ -1350,7 +1349,7 @@
     }
 
     public void snapToPageFromOverView(int whichPage) {
-        snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
+        snapToPage(whichPage, OVERVIEW.getTransitionDuration(mLauncher), Interpolators.ZOOM_IN);
     }
 
     private void onStartStateTransition(LauncherState state) {
@@ -1703,8 +1702,8 @@
             float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
             target.removeView(v);
 
-            FolderIcon fi =
-                mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
+            FolderIcon fi = mLauncher.addFolder(target, sourceInfo, container, screenId,
+                    targetCell[0], targetCell[1]);
             destInfo.cellX = -1;
             destInfo.cellY = -1;
             sourceInfo.cellX = -1;
@@ -1915,7 +1914,8 @@
                     // Animate the item to its original position, while simultaneously exiting
                     // spring-loaded mode so the page meets the icon where it was picked up.
                     mLauncher.getDragController().animateDragViewToOriginalPosition(
-                            onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS);
+                            onCompleteRunnable, cell,
+                            SPRING_LOADED.getTransitionDuration(mLauncher));
                     mLauncher.getStateManager().goToState(NORMAL);
                     mLauncher.getDropTargetBar().onDragEnd();
                     parent.onDropChild(cell);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index ed869bb..0b439ec 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -54,6 +54,7 @@
 
     public static final int REMOVE = R.id.action_remove;
     public static final int UNINSTALL = R.id.action_uninstall;
+    public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction;
     public static final int RECONFIGURE = R.id.action_reconfigure;
     protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
     protected static final int MOVE = R.id.action_move;
@@ -86,6 +87,8 @@
                 launcher.getText(R.string.remove_drop_target_label)));
         mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
                 launcher.getText(R.string.uninstall_drop_target_label)));
+        mActions.put(DISMISS_PREDICTION, new AccessibilityAction(DISMISS_PREDICTION,
+                launcher.getText(R.string.dismiss_prediction_label)));
         mActions.put(RECONFIGURE, new AccessibilityAction(RECONFIGURE,
                 launcher.getText(R.string.gadget_setup_text)));
         mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 0681919..1bde138 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -11,7 +11,7 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
 
 import android.animation.Animator;
@@ -46,7 +46,7 @@
  */
 public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
 
-    private static final float SPRING_DAMPING_RATIO = 0.9f;
+    private static final float SPRING_DAMPING_RATIO = 0.75f;
     private static final float SPRING_STIFFNESS = 600f;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
@@ -185,7 +185,7 @@
     }
 
     public Animator createSpringAnimation(float... progressValues) {
-        if (UNSTABLE_SPRINGS.get()) {
+        if (QUICKSTEP_SPRINGS.get()) {
             return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
                     SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
         }
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 91a3106..27b9c18 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -29,12 +29,13 @@
 import android.util.FloatProperty;
 import android.util.Log;
 
-import java.util.ArrayList;
-
 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
+import java.util.ArrayList;
+
+
 /**
  * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
  * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
@@ -137,11 +138,13 @@
 
         mProperty.switchToSpring();
 
-        mSpring.setStartVelocity(velocity);
-
         float startValue = end == 0 ? mValues[1] : mValues[0];
         float endValue = end == 0 ? mValues[0] : mValues[1];
-        mSpring.setStartValue(startValue);
+
+        // Ensures that the velocity matches the direction of the values.
+        velocity = Math.signum(endValue - startValue) * Math.abs(velocity);
+        mSpring.setStartVelocity(velocity);
+
         new Handler(Looper.getMainLooper()).postDelayed(() -> {
             mSpring.animateToFinalPosition(endValue);
         }, getStartDelay());
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index b1a2c33..cc33965 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -112,6 +112,9 @@
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
             "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
 
+    public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
+            "MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
+
     public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
             "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
             "Show launcher preview in grid picker");
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 09062a4..f71cfb8 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -29,9 +29,11 @@
 import android.os.Process;
 
 import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
@@ -86,7 +88,7 @@
         // Total duration for the drop animation to complete.
         long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) +
                 LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
-                LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+                LauncherState.SPRING_LOADED.getTransitionDuration(Launcher.getLauncher(mContext));
         // Delay the actual accept() call until the drop animation is complete.
         return PinRequestHelper.createWorkspaceItemFromPinItemRequest(
                 mContext, mRequest, duration);
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index f7ee5f9..372c591 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -31,7 +31,7 @@
     CharSequence getLabel(PackageManager pm);
 
 
-    class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
+    class ComponentCachingLogic<T extends ComponentWithLabel> implements CachingLogic<T> {
 
         private final PackageManager mPackageManager;
         private final boolean mAddToMemCache;
@@ -42,22 +42,22 @@
         }
 
         @Override
-        public ComponentName getComponent(ComponentWithLabel object) {
+        public ComponentName getComponent(T object) {
             return object.getComponent();
         }
 
         @Override
-        public UserHandle getUser(ComponentWithLabel object) {
+        public UserHandle getUser(T object) {
             return object.getUser();
         }
 
         @Override
-        public CharSequence getLabel(ComponentWithLabel object) {
+        public CharSequence getLabel(T object) {
             return object.getLabel(mPackageManager);
         }
 
         @Override
-        public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
+        public BitmapInfo loadIcon(Context context, T object) {
             return BitmapInfo.LOW_RES_INFO;
         }
 
diff --git a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
new file mode 100644
index 0000000..248a57d
--- /dev/null
+++ b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.icons;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.LauncherAppState;
+
+/**
+ * Extension of ComponentWithLabel to also support loading icons
+ */
+public interface ComponentWithLabelAndIcon extends ComponentWithLabel {
+
+    /**
+     * Provide an icon for this object
+     */
+    Drawable getFullResIcon(IconCache cache);
+
+    class ComponentWithIconCachingLogic extends ComponentCachingLogic<ComponentWithLabelAndIcon> {
+
+        public ComponentWithIconCachingLogic(Context context, boolean addToMemCache) {
+            super(context, addToMemCache);
+        }
+
+        @NonNull
+        @Override
+        public BitmapInfo loadIcon(Context context, ComponentWithLabelAndIcon object) {
+            Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
+                    .getIconCache());
+            if (d == null) {
+                return super.loadIcon(context, object);
+            }
+            try (LauncherIcons li = LauncherIcons.obtain(context)) {
+                return li.createBadgedIconBitmap(d, object.getUser(), 0);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 69b8125..804acc3 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -56,6 +57,7 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 
+import java.util.function.Predicate;
 import java.util.function.Supplier;
 
 /**
@@ -65,6 +67,9 @@
 
     private static final String TAG = "Launcher.IconCache";
 
+    private final Predicate<ItemInfoWithIcon> mIsUsingFallbackIconCheck = w -> w.bitmap != null
+            && w.bitmap.isNullOrLowRes() && !isDefaultIcon(w.bitmap, w.user);
+
     private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
     private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
     private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
@@ -180,6 +185,77 @@
     }
 
     /**
+     * Fill in {@param info} with the icon for {@param si}
+     */
+    public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
+        getShortcutIcon(info, si, true, mIsUsingFallbackIconCheck);
+    }
+
+    /**
+     * Fill in {@param info} with an unbadged icon for {@param si}
+     */
+    public void getUnbadgedShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
+        getShortcutIcon(info, si, false, mIsUsingFallbackIconCheck);
+    }
+
+    /**
+     * Fill in {@param info} with the icon and label for {@param si}. If the icon is not
+     * available, and fallback check returns true, it keeps the old icon.
+     */
+    public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
+            @NonNull Predicate<T> fallbackIconCheck) {
+        getShortcutIcon(info, si, true /* use badged */, fallbackIconCheck);
+    }
+
+    private synchronized <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
+            boolean useBadged, @NonNull Predicate<T> fallbackIconCheck) {
+        BitmapInfo bitmapInfo;
+        if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+            bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
+                    () -> si, mShortcutCachingLogic, false, false).bitmap;
+        } else {
+            // If caching is disabled, load the full icon
+            bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
+        }
+        if (bitmapInfo.isNullOrLowRes()) {
+            bitmapInfo = getDefaultIcon(si.getUserHandle());
+        }
+
+        if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
+            return;
+        }
+        info.bitmap = bitmapInfo;
+        if (useBadged) {
+            BitmapInfo badgeInfo = getShortcutInfoBadge(si);
+            try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+                info.bitmap = li.badgeBitmap(info.bitmap.icon, badgeInfo);
+            }
+        }
+    }
+
+    /**
+     * Returns the badging info for the shortcut
+     */
+    public BitmapInfo getShortcutInfoBadge(ShortcutInfo shortcutInfo) {
+        ComponentName cn = shortcutInfo.getActivity();
+        if (cn != null) {
+            // Get the app info for the source activity.
+            AppInfo appInfo = new AppInfo();
+            appInfo.user = shortcutInfo.getUserHandle();
+            appInfo.componentName = cn;
+            appInfo.intent = new Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .setComponent(cn);
+            getTitleAndIcon(appInfo, false);
+            return appInfo.bitmap;
+        } else {
+            PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
+            getTitleAndIconForApp(pkgInfo, false);
+            return pkgInfo.bitmap;
+        }
+    }
+
+    /**
      * Fill in info with the icon and label for deep shortcut.
      */
     public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) {
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index e67d244..cbd7c53 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,28 +16,11 @@
 
 package com.android.launcher3.icons;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Process;
 
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.util.Themes;
-
-import java.util.function.Supplier;
 
 /**
  * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -111,114 +94,4 @@
     public void close() {
         recycle();
     }
-
-    // below methods should also migrate to BaseIconFactory
-    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
-        return createShortcutIcon(shortcutInfo, true /* badged */);
-    }
-
-    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
-        return createShortcutIcon(shortcutInfo, badged, null);
-    }
-
-    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
-            @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
-        if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
-            return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
-        } else {
-            return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
-        }
-    }
-
-    public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
-            @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
-        Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
-                mContext, shortcutInfo, mFillResIconDpi);
-        IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
-        final Bitmap unbadgedBitmap;
-        if (unbadgedDrawable != null) {
-            unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
-        } else {
-            if (fallbackIconProvider != null) {
-                // Fallback icons are already badged and with appropriate shadow
-                ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
-                if (fullIcon != null && fullIcon.bitmap != null) {
-                    return fullIcon.bitmap;
-                }
-            }
-            unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
-        }
-
-        if (!badged) {
-            return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
-        }
-
-        final Bitmap unbadgedfinal = unbadgedBitmap;
-        final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
-        Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
-            getShadowGenerator().recreateIcon(unbadgedfinal, c);
-            badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
-        });
-        return BitmapInfo.of(icon, badge.bitmap.color);
-    }
-
-    @WorkerThread
-    public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
-            boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
-        IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
-        final BitmapInfo bitmapInfo;
-        if (useCache) {
-            bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
-        } else {
-            bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
-        }
-        final Bitmap unbadgedBitmap;
-        if (bitmapInfo.icon != null) {
-            unbadgedBitmap = bitmapInfo.icon;
-        } else {
-            if (fallbackIconProvider != null) {
-                // Fallback icons are already badged and with appropriate shadow
-                ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
-                if (fullIcon != null && fullIcon.bitmap != null) {
-                    return fullIcon.bitmap;
-                }
-            }
-            unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
-        }
-
-        if (!badged) {
-            return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
-        }
-
-        final Bitmap unbadgedfinal = unbadgedBitmap;
-        final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
-        Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
-            getShadowGenerator().recreateIcon(unbadgedfinal, c);
-            badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
-        });
-        return BitmapInfo.of(icon, badge.bitmap.color);
-    }
-
-    public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
-        ComponentName cn = shortcutInfo.getActivity();
-        String badgePkg = shortcutInfo.getPackage();
-        boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
-        if (cn != null && !hasBadgePkgSet) {
-            // Get the app info for the source activity.
-            AppInfo appInfo = new AppInfo();
-            appInfo.user = shortcutInfo.getUserHandle();
-            appInfo.componentName = cn;
-            appInfo.intent = new Intent(Intent.ACTION_MAIN)
-                    .addCategory(Intent.CATEGORY_LAUNCHER)
-                    .setComponent(cn);
-            cache.getTitleAndIcon(appInfo, false);
-            return appInfo;
-        } else {
-            PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
-            cache.getTitleAndIconForApp(pkgInfo, false);
-            return pkgInfo;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index b856dd1..d7eed06 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -25,6 +25,7 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -57,6 +58,12 @@
         return info.getShortLabel();
     }
 
+    @Override
+    public CharSequence getDescription(ShortcutInfo object, CharSequence fallback) {
+        CharSequence label = object.getLongLabel();
+        return TextUtils.isEmpty(label) ? fallback : label;
+    }
+
     @NonNull
     @Override
     public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index fc9948e..eb99af5 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -33,6 +33,8 @@
 import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.pm.UserCache;
 
+import java.util.Objects;
+
 /**
  * Helper class to backup and restore Favorites table into a separate table
  * within the same data base.
@@ -61,7 +63,8 @@
     private static final int STATE_SANITIZED = 2;
 
     private final Context mContext;
-    private final SQLiteDatabase mDb;
+    private final SQLiteDatabase mFavoritesDb;
+    private final SQLiteDatabase mBackupDb;
 
     private final int mOldHotseatSize;
     private final int mOldGridX;
@@ -74,10 +77,11 @@
     @IntDef({STATE_NOT_FOUND, STATE_RAW, STATE_SANITIZED})
     private @interface BackupState { }
 
-    public GridBackupTable(Context context, SQLiteDatabase db,
+    public GridBackupTable(Context context, SQLiteDatabase favoritesDb, SQLiteDatabase backupDb,
             int hotseatSize, int gridX, int gridY) {
         mContext = context;
-        mDb = db;
+        mFavoritesDb = favoritesDb;
+        mBackupDb = backupDb;
 
         mOldHotseatSize = hotseatSize;
         mOldGridX = gridX;
@@ -90,7 +94,7 @@
      */
     public boolean backupOrRestoreAsNeeded() {
         // Check if backup table exists
-        if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
+        if (!tableExists(mBackupDb, BACKUP_TABLE_NAME)) {
             if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
                     .getBoolean(Settings.EXTRA_VALUE, false)) {
                 // No need to copy if empty DB was created.
@@ -105,7 +109,7 @@
         }
         long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
                 Process.myUserHandle());
-        copyTable(BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
+        copyTable(mBackupDb, BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME, userSerial);
         Log.d(TAG, "Backup table found");
         return true;
     }
@@ -118,28 +122,37 @@
     /**
      * Copy valid grid entries from one table to another.
      */
-    private void copyTable(String from, String to, long userSerial) {
-        dropTable(mDb, to);
-        Favorites.addTableToDb(mDb, userSerial, false, to);
-        mDb.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
+    private static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
+            String toTable, long userSerial) {
+        dropTable(toDb, toTable);
+        Favorites.addTableToDb(toDb, userSerial, false, toTable);
+        if (fromDb != toDb) {
+            toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
+            toDb.execSQL(
+                    "INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable
+                            + " where _id > " + ID_PROPERTY);
+        } else {
+            toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable + " where _id > "
+                    + ID_PROPERTY);
+        }
     }
 
     private void encodeDBProperties(int options) {
         ContentValues values = new ContentValues();
         values.put(Favorites._ID, ID_PROPERTY);
-        values.put(KEY_DB_VERSION, mDb.getVersion());
+        values.put(KEY_DB_VERSION, mFavoritesDb.getVersion());
         values.put(KEY_GRID_X_SIZE, mOldGridX);
         values.put(KEY_GRID_Y_SIZE, mOldGridY);
         values.put(KEY_HOTSEAT_SIZE, mOldHotseatSize);
         values.put(Favorites.OPTIONS, options);
-        mDb.insert(BACKUP_TABLE_NAME, null, values);
+        mBackupDb.insert(BACKUP_TABLE_NAME, null, values);
     }
 
     /**
      * Load DB properties from grid backup table.
      */
     public @BackupState int loadDBProperties() {
-        try (Cursor c = mDb.query(BACKUP_TABLE_NAME, new String[] {
+        try (Cursor c = mBackupDb.query(BACKUP_TABLE_NAME, new String[] {
                 KEY_DB_VERSION,     // 0
                 KEY_GRID_X_SIZE,    // 1
                 KEY_GRID_Y_SIZE,    // 2
@@ -150,7 +163,7 @@
                 Log.e(TAG, "Meta data not found in backup table");
                 return STATE_NOT_FOUND;
             }
-            if (!validateDBVersion(mDb.getVersion(), c.getInt(0))) {
+            if (!validateDBVersion(mBackupDb.getVersion(), c.getInt(0))) {
                 return STATE_NOT_FOUND;
             }
 
@@ -166,7 +179,7 @@
      * Restore workspace from raw backup if available.
      */
     public boolean restoreFromRawBackupIfAvailable(long oldProfileId) {
-        if (!tableExists(mDb, Favorites.BACKUP_TABLE_NAME)
+        if (!tableExists(mBackupDb, Favorites.BACKUP_TABLE_NAME)
                 || loadDBProperties() != STATE_RAW
                 || mOldHotseatSize != mRestoredHotseatSize
                 || mOldGridX != mRestoredGridX
@@ -174,7 +187,8 @@
             // skip restore if dimensions in backup table differs from current setup.
             return false;
         }
-        copyTable(Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
+        copyTable(mBackupDb, Favorites.BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME,
+                oldProfileId);
         Log.d(TAG, "Backup restored");
         return true;
     }
@@ -183,7 +197,8 @@
      * Performs a backup on the workspace layout.
      */
     public void doBackup(long profileId, int options) {
-        copyTable(Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
+        copyTable(mFavoritesDb, Favorites.TABLE_NAME, mBackupDb, Favorites.BACKUP_TABLE_NAME,
+                profileId);
         encodeDBProperties(options);
     }
 
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index c35c4b9..4f92066 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -909,7 +909,7 @@
             boolean dbChanged = false;
 
             GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
-                    srcHotseatCount, sourceSize.x, sourceSize.y);
+                    transaction.getDb(), srcHotseatCount, sourceSize.x, sourceSize.y);
             if (backupTable.backupOrRestoreAsNeeded()) {
                 dbChanged = true;
                 srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
new file mode 100644
index 0000000..63b7191
--- /dev/null
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+/**
+ * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
+ * result of restoring from a larger device or device density change.
+ */
+public class GridSizeMigrationTaskV2 {
+
+    private GridSizeMigrationTaskV2(Context context) {
+
+    }
+
+    /**
+     * Migrates the workspace and hotseat in case their sizes changed.
+     *
+     * @return false if the migration failed.
+     */
+    public static boolean migrateGridIfNeeded(Context context) {
+        // To be implemented.
+        return true;
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 95268d0..4961432 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.model;
 
+import static android.graphics.BitmapFactory.decodeByteArray;
+
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
@@ -26,7 +28,6 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.CursorWrapper;
-import android.graphics.BitmapFactory;
 import android.os.UserHandle;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
@@ -166,37 +167,30 @@
      */
     protected boolean loadIcon(WorkspaceItemInfo info) {
         try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
-            return loadIcon(info, li);
-        }
-    }
-
-    /**
-     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
-     */
-    protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
-        if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-            String packageName = getString(iconPackageIndex);
-            String resourceName = getString(iconResourceIndex);
-            if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
-                info.iconResource = new ShortcutIconResource();
-                info.iconResource.packageName = packageName;
-                info.iconResource.resourceName = resourceName;
-                BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
-                if (iconInfo != null) {
-                    info.bitmap = iconInfo;
-                    return true;
+            if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                String packageName = getString(iconPackageIndex);
+                String resourceName = getString(iconResourceIndex);
+                if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+                    info.iconResource = new ShortcutIconResource();
+                    info.iconResource.packageName = packageName;
+                    info.iconResource.resourceName = resourceName;
+                    BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
+                    if (iconInfo != null) {
+                        info.bitmap = iconInfo;
+                        return true;
+                    }
                 }
             }
-        }
 
-        // Failed to load from resource, try loading from DB.
-        byte[] data = getBlob(iconIndex);
-        try {
-            info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
-            return true;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to decode byte array for info " + info, e);
-            return false;
+            // Failed to load from resource, try loading from DB.
+            byte[] data = getBlob(iconIndex);
+            try {
+                info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
+                return true;
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to decode byte array for info " + info, e);
+                return false;
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 23ec459..8dc7a3a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -35,7 +36,6 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.ShortcutInfo;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
@@ -50,7 +50,6 @@
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
@@ -61,11 +60,10 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderNameProvider;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherActivityCachingLogic;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
@@ -94,7 +92,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CancellationException;
-import java.util.function.Supplier;
 
 /**
  * Runnable for the thread that loads the contents of the launcher:
@@ -263,7 +260,8 @@
             verifyNotStopped();
 
             // fourth step
-            List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
+            List<ComponentWithLabelAndIcon> allWidgetsList =
+                    mBgDataModel.widgetsModel.update(mApp, null);
             logger.addSplit("load widgets");
 
             verifyNotStopped();
@@ -271,8 +269,9 @@
             logger.addSplit("bindWidgets");
             verifyNotStopped();
 
-            updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
-                    mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
+            updateHandler.updateIcons(allWidgetsList,
+                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
+                    mApp.getModel()::onWidgetLabelsUpdated);
             logger.addSplit("save widgets in icon cache");
 
             // fifth step
@@ -320,7 +319,9 @@
             clearDb = true;
         }
 
-        if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+        if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
+                ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
+                : !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
             // Migration failed. Clear workspace.
             clearDb = true;
         }
@@ -544,17 +545,10 @@
                                         continue;
                                     }
                                     info = new WorkspaceItemInfo(pinnedShortcut, context);
-                                    final WorkspaceItemInfo finalInfo = info;
-
-                                    LauncherIcons li = LauncherIcons.obtain(context);
                                     // If the pinned deep shortcut is no longer published,
                                     // use the last saved icon instead of the default.
-                                    Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
-                                            c.loadIcon(finalInfo, li) ? finalInfo : null;
-                                    info.bitmap = li.createShortcutIcon(
-                                            pinnedShortcut, true /* badged */,
-                                            fallbackIconProvider);
-                                    li.recycle();
+                                    mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
+
                                     if (pmHelper.isAppSuspended(
                                             pinnedShortcut.getPackage(), info.user)) {
                                         info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
@@ -854,23 +848,23 @@
 
     private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
         // Ignore packages which have a promise icon.
-        HashSet<String> packagesToIgnore = new HashSet<>();
         synchronized (mBgDataModel) {
             for (ItemInfo info : mBgDataModel.itemsIdMap) {
                 if (info instanceof WorkspaceItemInfo) {
                     WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     if (si.isPromise() && si.getTargetComponent() != null) {
-                        packagesToIgnore.add(si.getTargetComponent().getPackageName());
+                        updateHandler.addPackagesToIgnore(
+                                si.user, si.getTargetComponent().getPackageName());
                     }
                 } else if (info instanceof LauncherAppWidgetInfo) {
                     LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
                     if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
-                        packagesToIgnore.add(lawi.providerName.getPackageName());
+                        updateHandler.addPackagesToIgnore(
+                                lawi.user, lawi.providerName.getPackageName());
                     }
                 }
             }
         }
-        updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
     }
 
     private List<LauncherActivityInfo> loadAllApps() {
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index b0e7a69..3f79ad0 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -23,7 +23,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -89,12 +88,7 @@
                 }
                 for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
                     workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
-                    // If the shortcut is pinned but no longer has an icon in the system,
-                    // keep the current icon instead of reverting to the default icon.
-                    LauncherIcons li = LauncherIcons.obtain(context);
-                    workspaceItemInfo.bitmap = li.createShortcutIcon(
-                            fullDetails, true, () -> workspaceItemInfo);
-                    li.recycle();
+                    app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
                     updatedWorkspaceItemInfos.add(workspaceItemInfo);
                 }
             }
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index d527423..a3adc82 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -26,7 +26,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
@@ -89,11 +88,7 @@
                     }
                     si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
                     si.updateFromDeepShortcutInfo(shortcut, context);
-                    // If the shortcut is pinned but no longer has an icon in the system,
-                    // keep the current icon instead of reverting to the default icon.
-                    LauncherIcons li = LauncherIcons.obtain(context);
-                    si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
-                    li.recycle();
+                    app.getIconCache().getShortcutIcon(si, shortcut);
                 } else {
                     si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                 }
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index a15399d..74a5a31 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.pm;
 
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -32,8 +31,7 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.ShortcutCachingLogic;
 
 public class PinRequestHelper {
 
@@ -82,16 +80,10 @@
 
             ShortcutInfo si = request.getShortcutInfo();
             WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
-            // Apply the unbadged icon and fetch the actual icon asynchronously.
-            if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
-                fetchAndUpdateShortcutIconAsync(context, info, si, false);
-            } else {
-                LauncherIcons li = LauncherIcons.obtain(context);
-                info.bitmap = li.createShortcutIcon(si, false /* badged */);
-                li.recycle();
-                LauncherAppState.getInstance(context).getModel()
-                        .updateAndBindWorkspaceItem(info, si);
-            }
+            // Apply the unbadged icon synchronously using the caching logic directly and
+            // fetch the actual icon asynchronously.
+            info.bitmap = new ShortcutCachingLogic().loadIcon(context, si);
+            LauncherAppState.getInstance(context).getModel().updateAndBindWorkspaceItem(info, si);
             return info;
         } else {
             return null;
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index b563171..ac0e065 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -41,7 +41,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -51,7 +51,7 @@
 /**
  * Wrapper class for representing a shortcut configure activity.
  */
-public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
+public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAndIcon {
 
     private static final String TAG = "SCActivityInfo";
 
@@ -77,6 +77,7 @@
         return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
 
+    @Override
     public abstract Drawable getFullResIcon(IconCache cache);
 
     /**
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 9faeb40..fdcf04f 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -26,8 +26,9 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
@@ -153,13 +154,11 @@
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
+            IconCache cache = LauncherAppState.getInstance(launcher).getIconCache();
             for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
                 final ShortcutInfo shortcut = shortcuts.get(i);
                 final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
-                // Use unbadged icon for the menu.
-                LauncherIcons li = LauncherIcons.obtain(launcher);
-                si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
-                li.recycle();
+                cache.getUnbadgedShortcutIcon(si, shortcut);
                 si.rank = i;
 
                 final DeepShortcutView view = shortcutViews.get(i);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index b152954..21c5ac5 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -16,14 +16,10 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -176,33 +172,6 @@
         }
     }
 
-    public static final Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
-        if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
-        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION
-                && itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            return null;
-        }
-        return new DismissPrediction(launcher, itemInfo);
-    };
-
-    public static class DismissPrediction extends SystemShortcut<Launcher> {
-        public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
-            super(R.drawable.ic_block, R.string.dismiss_prediction_label, launcher,
-                    itemInfo);
-        }
-
-        @Override
-        public void onClick(View view) {
-            PopupContainerWithArrow.closeAllOpenViews(mTarget);
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
-            AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
-                    mItemInfo.getTargetComponent(),
-                    mItemInfo.user,
-                    AppLaunchTracker.CONTAINER_PREDICTIONS);
-        }
-    }
-
     public static void dismissTaskMenuView(BaseDraggingActivity activity) {
         AbstractFloatingView.closeOpenViews(activity, true,
             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 407ff31..842be40 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -107,15 +107,17 @@
      */
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+        // TODO(pinyaoting): Support backing up workspace with multiple grid options.
+        new GridBackupTable(context, db, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
 
     private void restoreWorkspace(@NonNull Context context, @NonNull SQLiteDatabase db,
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
             throws Exception {
+        // TODO(pinyaoting): Support restoring workspace with multiple grid options.
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        GridBackupTable backupTable = new GridBackupTable(context, db,
+        GridBackupTable backupTable = new GridBackupTable(context, db, db,
                 idp.numHotseatIcons, idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
             sanitizeDB(helper, db, backupManager);
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index cb56097..290dbb6 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.states;
 
-import static com.android.launcher3.LauncherAnimUtils.HINT_TRANSITION_MS;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
@@ -31,7 +29,12 @@
             | FLAG_HAS_SYS_UI_SCRIM;
 
     public HintState(int id) {
-        super(id, ContainerType.DEFAULT_CONTAINERTYPE, HINT_TRANSITION_MS, STATE_FLAGS);
+        super(id, ContainerType.DEFAULT_CONTAINERTYPE, STATE_FLAGS);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher launcher) {
+        return 80;
     }
 
     @Override
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index be3e6c9..97c67c5 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.states;
 
-import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 
 import android.graphics.Rect;
@@ -37,7 +36,12 @@
             FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS | FLAG_HIDE_BACK_BUTTON;
 
     public SpringLoadedState(int id) {
-        super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
+        super(id, ContainerType.OVERVIEW, STATE_FLAGS);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher launcher) {
+        return 150;
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 3ec480d..7ae0526 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -23,7 +23,7 @@
 import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.Animator;
@@ -380,6 +380,7 @@
 
         final LauncherState targetState;
         final float progress = mCurrentAnimation.getProgressFraction();
+        final float progressVelocity = velocity * mProgressMultiplier;
         final float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
         if (fling) {
             targetState =
@@ -406,7 +407,7 @@
                 startProgress = 1;
             } else {
                 startProgress = Utilities.boundToRange(progress
-                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
+                        + progressVelocity * getSingleFrameMs(mLauncher), 0f, 1f);
                 duration = BaseSwipeDetector.calculateDuration(velocity,
                         endProgress - Math.max(progress, 0)) * durationMultiplier;
             }
@@ -421,7 +422,7 @@
                 startProgress = 0;
             } else {
                 startProgress = Utilities.boundToRange(progress
-                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
+                        + progressVelocity * getSingleFrameMs(mLauncher), 0f, 1f);
                 duration = BaseSwipeDetector.calculateDuration(velocity,
                         Math.min(progress, 1) - endProgress) * durationMultiplier;
             }
@@ -433,8 +434,8 @@
         maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
         updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
                 targetState, velocity, fling);
-        mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity);
-        if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+        mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, progressVelocity);
+        if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index a03b743..49c97da 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,19 +15,10 @@
  */
 package com.android.launcher3.util;
 
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.content.pm.ShortcutInfo;
-
-import androidx.annotation.NonNull;
-
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
@@ -70,21 +61,6 @@
                 && info instanceof WorkspaceItemInfo;
     }
 
-    /**
-     * Fetch the shortcut icon in background, then update the UI.
-     */
-    public static void fetchAndUpdateShortcutIconAsync(
-            @NonNull Context context, @NonNull WorkspaceItemInfo info, @NonNull ShortcutInfo si,
-            boolean badged) {
-        MODEL_EXECUTOR.execute(() -> {
-            try (LauncherIcons li = LauncherIcons.obtain(context)) {
-                info.bitmap = li.createShortcutIcon(si, badged, null);
-                LauncherAppState.getInstance(context).getModel()
-                        .updateAndBindWorkspaceItem(info, si);
-            }
-        });
-    }
-
     private static boolean isActive(ItemInfo info) {
         boolean isLoading = info instanceof WorkspaceItemInfo
                 && ((WorkspaceItemInfo) info).hasPromiseIconUi();
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 22c0874..5ebf8d3 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -21,7 +21,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.util.MultiHashMap;
@@ -85,12 +85,13 @@
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
      */
-    public List<ComponentWithLabel> update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
+    public List<ComponentWithLabelAndIcon> update(
+            LauncherAppState app, @Nullable PackageUserKey packageUser) {
         Preconditions.assertWorkerThread();
 
         Context context = app.getContext();
         final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
-        List<ComponentWithLabel> updatedItems = new ArrayList<>();
+        List<ComponentWithLabelAndIcon> updatedItems = new ArrayList<>();
         try {
             InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
             PackageManager pm = app.getContext().getPackageManager();
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index 7006d77..a56801f 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
@@ -42,7 +41,12 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
+        super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher context) {
+        return 320;
     }
 
     @Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index aeba788..e20b2ca 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,8 +15,7 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
@@ -26,7 +25,12 @@
 public class OverviewState extends LauncherState {
 
     public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, FLAG_DISABLE_RESTORE);
+        super(id, ContainerType.WORKSPACE, FLAG_DISABLE_RESTORE);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher context) {
+        return 250;
     }
 
     public static OverviewState newBackgroundState(int id) {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index de9757f..4b1a067 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,12 +71,14 @@
 
     @Test
     @PortraitLandscape
+    @Ignore // b/148867106
     public void testWidgetConfig() throws Throwable {
         runTest(true);
     }
 
     @Test
     @PortraitLandscape
+    @Ignore // b/148867106
     public void testConfigCancelled() throws Throwable {
         runTest(false);
     }
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
index 695b36c..5880eb6 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
@@ -55,7 +55,7 @@
     private static final ExceptionMatch[] EXCEPTION_MATCHES = {
             new ExceptionMatch(
                     "java.lang.AssertionError: http://go/tapl : Tests are broken by a "
-                            + "non-Launcher system error: Phone is locked",
+                            + "non-Launcher system error: (Phone is locked|Screen is empty)",
                     new LogcatMatch[]{
                             new LogcatMatch(
                                     "BroadcastQueue: Can't deliver broadcast to com.android"
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 7763cc2..02d07bb 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -78,10 +78,14 @@
                 try {
                     base.evaluate();
                 } catch (Throwable e) {
-                    if (!Log.getStackTraceString(e).contains(
+                    final String stackTrace = Log.getStackTraceString(e);
+                    if (!stackTrace.contains(
                             "androidx.test.internal.runner.junit4.statement.RunBefores.evaluate")) {
                         // Test failed to deinitialize. Since the global state is probably
                         // corrupted, won't execute other tests.
+                        Log.d(TAG,
+                                "Detected an exception from test finalizer, will skip further "
+                                        + "tests: " + stackTrace);
                         sHadFailedTestDeinitialization = true;
                     }
                     throw e;
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 1feb4e7..61f5e05 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -73,8 +73,7 @@
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    if (sRunFlavor == 0) sRunFlavor = getRunFlavor();
-                    if ((stability.flavors() & sRunFlavor) != 0) {
+                    if ((stability.flavors() & getRunFlavor()) != 0) {
                         Log.d(TAG, "Running " + description.getDisplayName());
                         base.evaluate();
                     } else {
@@ -87,7 +86,9 @@
         }
     }
 
-    private static int getRunFlavor() {
+    public static int getRunFlavor() {
+        if (sRunFlavor != 0) return sRunFlavor;
+
         final String flavorOverride = InstrumentationRegistry.getArguments().getString("flavor");
 
         if (flavorOverride != null) {
@@ -130,34 +131,32 @@
             throw new AssertionError("Platform build match not found");
         }
 
-        final int runFlavor;
-
         if (launcherBuildMatcher.group("local") != null && (
                 platformBuildMatcher.group("commandLine") != null ||
                         platformBuildMatcher.group("postsubmit") != null)) {
             Log.d(TAG, "LOCAL RUN");
-            runFlavor = LOCAL;
+            sRunFlavor = LOCAL;
         } else if (launcherBuildMatcher.group("presubmit") != null
                 && platformBuildMatcher.group("postsubmit") != null) {
             Log.d(TAG, "UNBUNDLED PRESUBMIT");
-            runFlavor = UNBUNDLED_PRESUBMIT;
+            sRunFlavor = UNBUNDLED_PRESUBMIT;
         } else if (launcherBuildMatcher.group("postsubmit") != null
                 && platformBuildMatcher.group("postsubmit") != null) {
             Log.d(TAG, "UNBUNDLED POSTSUBMIT");
-            runFlavor = UNBUNDLED_POSTSUBMIT;
+            sRunFlavor = UNBUNDLED_POSTSUBMIT;
         } else if (launcherBuildMatcher.group("platform") != null
                 && platformBuildMatcher.group("presubmit") != null) {
             Log.d(TAG, "PLATFORM PRESUBMIT");
-            runFlavor = PLATFORM_PRESUBMIT;
+            sRunFlavor = PLATFORM_PRESUBMIT;
         } else if (launcherBuildMatcher.group("platform") != null
                 && (platformBuildMatcher.group("postsubmit") != null
                 || platformBuildMatcher.group("commandLine") != null)) {
             Log.d(TAG, "PLATFORM POSTSUBMIT");
-            runFlavor = PLATFORM_POSTSUBMIT;
+            sRunFlavor = PLATFORM_POSTSUBMIT;
         } else {
             throw new AssertionError("Unrecognized run flavor");
         }
 
-        return runFlavor;
+        return sRunFlavor;
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index cf5b24e..5eb164e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -106,6 +106,7 @@
     private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
     private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
+    private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -340,7 +341,8 @@
     public void checkForAnomaly() {
         final String anomalyMessage = getAnomalyMessage();
         if (anomalyMessage != null) {
-            String message = "Tests are broken by a non-Launcher system error: " + anomalyMessage;
+            String message = "http://go/tapl : Tests are broken by a non-Launcher system error: "
+                    + anomalyMessage;
             log("Hierarchy dump for: " + message);
             dumpViewHierarchy();
 
@@ -488,6 +490,12 @@
         assertEquals("Unexpected display rotation",
                 mExpectedRotation, mDevice.getDisplayRotation());
 
+        // b/149024111
+        for (int i = 0; i != 100; ++i) {
+            if (getNavigationModeMismatchError() == null) break;
+            sleep(100);
+        }
+
         final String error = getNavigationModeMismatchError();
         assertTrue(error, error == null);
         log("verifyContainerType: " + containerType);
@@ -633,6 +641,15 @@
                 log(action = "clicking home button from " + getVisibleStateMessage());
                 try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
                     mDevice.waitForIdle();
+
+                    if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
+                        if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
+                                hasLauncherObject(WIDGETS_RES_ID)
+                                        && !mDevice.isNaturalOrientation()) {
+                            expectEvent(EVENT_PILFER_POINTERS);
+                        }
+                    }
+
                     runToState(
                             waitForSystemUiObject("home")::click,
                             NORMAL_STATE_ORDINAL,
@@ -1051,16 +1068,21 @@
 
     void sendPointer(long downTime, long currentTime, int action, Point point,
             GestureScope gestureScope) {
-        if (gestureScope != GestureScope.OUTSIDE) {
-            switch (action) {
-                case MotionEvent.ACTION_DOWN:
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                if (gestureScope != GestureScope.OUTSIDE) {
                     expectEvent(EVENT_TOUCH_DOWN);
-                    break;
-                case MotionEvent.ACTION_UP:
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (gestureScope != GestureScope.INSIDE) {
+                    expectEvent(EVENT_PILFER_POINTERS);
+                }
+                if (gestureScope != GestureScope.OUTSIDE) {
                     expectEvent(gestureScope == GestureScope.INSIDE
                             ? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
-                    break;
-            }
+                }
+                break;
         }
 
         final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
@@ -1226,6 +1248,12 @@
     }
 
     Closable eventsCheck() {
+        if ("com.android.launcher3".equals(getLauncherPackageName())) {
+            // Not checking specific Launcher3 event sequences.
+            return () -> {
+            };
+        }
+
         // Entering events check block.
         startRecordingEvents();