Merge changes from topic "taskbar-hotseat" into sc-dev

* changes:
  Update taskbar to add extra hotseat items instead of recent apps
  Add support for having more hotseat icons in the DB than we show
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
deleted file mode 100644
index 87649f7..0000000
--- a/quickstep/res/layout/taskbar_divider.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<View
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/taskbar_divider_thickness"
-    android:layout_height="@dimen/taskbar_divider_height"
-    android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 54730f1..3bc8ddc 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,5 +27,4 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">#101010</color>
-    <color name="taskbar_divider">#C0C0C0</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3e6c78f..99be502 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -134,7 +134,5 @@
     <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
     <dimen name="taskbar_icon_spacing">8dp</dimen>
-    <dimen name="taskbar_divider_thickness">1dp</dimen>
-    <dimen name="taskbar_divider_height">32dp</dimen>
     <dimen name="taskbar_folder_margin">16dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4415b51..0524b21 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -108,6 +108,7 @@
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
         if (mTaskbarController != null) {
             mTaskbarController.cleanup();
+            mTaskbarController = null;
         }
 
         super.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 4451e7a..a6844e4 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -89,7 +89,7 @@
         ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
 
         //separate folders and items that can get in folders
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View view = mHotseat.getChildAt(i, 0);
             if (view == null) continue;
             ItemInfo info = (ItemInfo) view.getTag();
@@ -188,7 +188,7 @@
                     .getInt(LauncherSettings.Settings.EXTRA_VALUE);
             mNewScreens = IntArray.wrap(pageId);
         }
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View child = mHotseat.getChildAt(i, 0);
             if (child == null || child.getTag() == null) continue;
             ItemInfo tag = (ItemInfo) child.getTag();
@@ -224,7 +224,7 @@
 
     void showDimissTip() {
         if (mHotseat.getShortcutsAndWidgets().getChildCount()
-                < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
+                < mLauncher.getDeviceProfile().numShownHotseatIcons) {
             Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
                     R.string.hotseat_prediction_settings, null,
                     () -> mLauncher.startActivity(getSettingsIntent()));
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index c3677ea..a2ed211 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -88,7 +88,7 @@
         Rect padding = grid.getHotseatLayoutPadding();
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
-        mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
+        mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
         mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
 
         Button turnOnBtn = findViewById(R.id.turn_predictions_on);
@@ -178,7 +178,7 @@
     }
 
     private void populatePreview(List<WorkspaceItemInfo> predictions) {
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             WorkspaceItemInfo info = predictions.get(i);
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
             icon.setEnabled(false);
@@ -194,7 +194,7 @@
      */
     public void show(List<WorkspaceItemInfo> predictions) {
         if (getParent() != null
-                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
+                || predictions.size() < mLauncher.getDeviceProfile().numShownHotseatIcons
                 || mHotseatEduController == null) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index f297343..67ed5fb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -33,10 +33,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Hotseat;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -68,7 +68,7 @@
  * pinning of predicted apps and manages replacement of predicted apps with user drag.
  */
 public class HotseatPredictionController implements DragController.DragListener,
-        SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
+        SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener,
         DragSource, ViewGroup.OnHierarchyChangeListener {
 
     private static final int FLAG_UPDATE_PAUSED = 1 << 0;
@@ -115,10 +115,10 @@
     public HotseatPredictionController(QuickstepLauncher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
-        mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+        mHotSeatItemsCount = mLauncher.getDeviceProfile().numShownHotseatIcons;
         mLauncher.getDragController().addDragListener(this);
 
-        launcher.getDeviceProfile().inv.addOnChangeListener(this);
+        launcher.addOnDeviceProfileChangeListener(this);
         mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
     }
 
@@ -281,7 +281,7 @@
      * Unregisters callbacks and frees resources
      */
     public void destroy() {
-        mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
+        mLauncher.removeOnDeviceProfileChangeListener(this);
     }
 
     /**
@@ -446,8 +446,8 @@
     }
 
     @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        this.mHotSeatItemsCount = profile.numHotseatIcons;
+    public void onDeviceProfileChanged(DeviceProfile profile) {
+        this.mHotSeatItemsCount = profile.numShownHotseatIcons;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 90f762e..4956fa1 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -44,7 +44,7 @@
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
                 InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
                 GridBackupTable backupTable = new GridBackupTable(context,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
                 transaction.commit();
@@ -69,7 +69,7 @@
                 }
                 InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
                 GridBackupTable backupTable = new GridBackupTable(context,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
                 transaction.commit();
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index df3657d..8c68872 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -111,7 +111,7 @@
         mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
 
         WorkspaceItemFactory hotseatFactory =
-                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numHotseatIcons);
+                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
         mHotseatState.items.setItems(
                 mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
         mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
@@ -211,7 +211,7 @@
         registerPredictor(mHotseatState, apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(context)
                         .setUiSurface("hotseat")
-                        .setPredictedTargetCount(mIDP.numHotseatIcons)
+                        .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons)
                         .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
                         .build()));
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 559ede1..d772964 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -25,7 +25,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityOptions;
-import android.content.ComponentName;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -56,9 +55,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
  */
@@ -76,17 +72,11 @@
     private final TaskbarStateHandler mTaskbarStateHandler;
     private final TaskbarAnimationController mTaskbarAnimationController;
     private final TaskbarHotseatController mHotseatController;
-    private final TaskbarRecentsController mRecentsController;
     private final TaskbarDragController mDragController;
 
     // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
 
-    // Contains all loaded Tasks, not yet deduped from Hotseat items.
-    private List<Task> mLatestLoadedRecentTasks;
-    // Contains all loaded Hotseat items.
-    private ItemInfo[] mLatestLoadedHotseatItems;
-
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
 
@@ -106,8 +96,6 @@
                 createTaskbarAnimationControllerCallbacks());
         mHotseatController = new TaskbarHotseatController(mLauncher,
                 createTaskbarHotseatControllerCallbacks());
-        mRecentsController = new TaskbarRecentsController(mLauncher,
-                createTaskbarRecentsControllerCallbacks());
         mDragController = new TaskbarDragController(mLauncher);
     }
 
@@ -220,24 +208,6 @@
             @Override
             public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
                 mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos);
-                mLatestLoadedHotseatItems = hotseatItemInfos;
-                dedupeAndUpdateRecentItems();
-            }
-        };
-    }
-
-    private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() {
-        return new TaskbarRecentsControllerCallbacks() {
-            @Override
-            public void updateRecentItems(ArrayList<Task> recentTasks) {
-                mLatestLoadedRecentTasks = recentTasks;
-                dedupeAndUpdateRecentItems();
-            }
-
-            @Override
-            public void updateRecentTaskAtIndex(int taskIndex, Task task) {
-                mTaskbarViewInApp.updateRecentTaskAtIndex(taskIndex, task);
-                mTaskbarViewOnHome.updateRecentTaskAtIndex(taskIndex, task);
             }
         };
     }
@@ -246,16 +216,13 @@
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
-        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(),
-                mRecentsController.getNumRecentIcons());
-        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(),
-                mRecentsController.getNumRecentIcons());
+        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
+        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
         mTaskbarContainerView.init(mTaskbarViewInApp);
         addToWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
         mTaskbarAnimationController.init();
         mHotseatController.init();
-        mRecentsController.init();
 
         setWhichTaskbarViewIsVisible(mLauncher.hasBeenResumed()
                 ? mTaskbarViewOnHome
@@ -292,7 +259,6 @@
         mTaskbarStateHandler.setTaskbarCallbacks(null);
         mTaskbarAnimationController.cleanup();
         mHotseatController.cleanup();
-        mRecentsController.cleanup();
 
         setWhichTaskbarViewIsVisible(null);
     }
@@ -425,53 +391,6 @@
         return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
     }
 
-    private void dedupeAndUpdateRecentItems() {
-        if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) {
-            return;
-        }
-
-        final int numRecentIcons = mRecentsController.getNumRecentIcons();
-
-        // From most recent to least recently opened.
-        List<Task> dedupedTasksInDescendingOrder = new ArrayList<>();
-        for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) {
-            Task task = mLatestLoadedRecentTasks.get(i);
-            boolean isTaskInHotseat = false;
-            for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) {
-                if (hotseatItem == null) {
-                    continue;
-                }
-                ComponentName hotseatActivity = hotseatItem.getTargetComponent();
-                if (hotseatActivity != null && task.key.sourceComponent.getPackageName()
-                        .equals(hotseatActivity.getPackageName())) {
-                    isTaskInHotseat = true;
-                    break;
-                }
-            }
-            if (!isTaskInHotseat) {
-                dedupedTasksInDescendingOrder.add(task);
-                if (dedupedTasksInDescendingOrder.size() == numRecentIcons) {
-                    break;
-                }
-            }
-        }
-
-        // TaskbarView expects an array of all the recent tasks to show, in the order to show them.
-        // So we create an array of the proper size, then fill it in such that the most recent items
-        // are at the end. If there aren't enough elements to fill the array, leave them null.
-        Task[] tasksArray = new Task[numRecentIcons];
-        for (int i = 0; i < tasksArray.length; i++) {
-            Task task = i >= dedupedTasksInDescendingOrder.size()
-                    ? null
-                    : dedupedTasksInDescendingOrder.get(i);
-            tasksArray[tasksArray.length - 1 - i] = task;
-        }
-
-        mTaskbarViewInApp.updateRecentTasks(tasksArray);
-        mTaskbarViewOnHome.updateRecentTasks(tasksArray);
-        mRecentsController.loadIconsForTasks(tasksArray);
-    }
-
     /**
      * @return Whether the given View is in the same window as Taskbar.
      */
@@ -573,12 +492,4 @@
     protected interface TaskbarHotseatControllerCallbacks {
         void updateHotseatItems(ItemInfo[] hotseatItemInfos);
     }
-
-    /**
-     * Contains methods that TaskbarRecentsController can call to interface with TaskbarController.
-     */
-    protected interface TaskbarRecentsControllerCallbacks {
-        void updateRecentItems(ArrayList<Task> recentTasks);
-        void updateRecentTaskAtIndex(int taskIndex, Task task);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
index b1bafdb..68829cd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -52,7 +52,7 @@
         mLauncher = launcher;
         mHotseat = mLauncher.getHotseat();
         mTaskbarCallbacks = taskbarCallbacks;
-        mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+        mNumHotseatIcons = mLauncher.getDeviceProfile().numShownHotseatIcons;
     }
 
     protected void init() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
deleted file mode 100644
index 4256d2b..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.util.CancellableTask;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-
-import java.util.ArrayList;
-
-/**
- * Works with TaskbarController to update the TaskbarView's Recent items.
- */
-public class TaskbarRecentsController {
-
-    private final int mNumRecentIcons = 2;
-    private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks;
-    private final RecentsModel mRecentsModel;
-
-    private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() {
-        @Override
-        public void onTaskStackChanged() {
-            reloadRecentTasksIfNeeded();
-        }
-    };
-
-    // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?)
-
-    // Used to keep track of the last requested task list id, so that we do not request to load the
-    // tasks again if we have already requested it and the task list has not changed
-    private int mTaskListChangeId = -1;
-
-    // The current background requests to load the task icons
-    private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
-
-    private boolean mIsAlive;
-
-    public TaskbarRecentsController(BaseQuickstepLauncher launcher,
-            TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
-        mLauncher = launcher;
-        mTaskbarCallbacks = taskbarCallbacks;
-        mRecentsModel = RecentsModel.INSTANCE.get(mLauncher);
-    }
-
-    protected void init() {
-        mIsAlive = true;
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
-        reloadRecentTasksIfNeeded();
-    }
-
-    protected void cleanup() {
-        mIsAlive = false;
-        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
-                mTaskStackChangeListener);
-        cancelAllPendingIconLoadTasks();
-    }
-
-    private void reloadRecentTasksIfNeeded() {
-        if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) {
-            mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged);
-        }
-    }
-
-    private void cancelAllPendingIconLoadTasks() {
-        for (int i = 0; i < mIconLoadRequests.length; i++) {
-            if (mIconLoadRequests[i] != null) {
-                mIconLoadRequests[i].cancel();
-            }
-            mIconLoadRequests[i] = null;
-        }
-    }
-
-    private void onRecentTasksChanged(ArrayList<Task> tasks) {
-        if (mIsAlive) {
-            mTaskbarCallbacks.updateRecentItems(tasks);
-        }
-    }
-
-    /**
-     * For each Task, loads its icon from the cache in the background, then calls
-     * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}.
-     */
-    protected void loadIconsForTasks(Task[] tasks) {
-        cancelAllPendingIconLoadTasks();
-        for (int i = 0; i < tasks.length; i++) {
-            Task task = tasks[i];
-            if (task == null) {
-                continue;
-            }
-            final int taskIndex = i;
-            mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground(
-                    task, updatedTask -> onTaskIconLoaded(task, taskIndex));
-        }
-    }
-
-    private void onTaskIconLoaded(Task task, int taskIndex) {
-        mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task);
-    }
-
-    protected int getNumRecentIcons() {
-        return mNumRecentIcons;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 21a2d51..f254844 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.DragEvent;
 import android.view.MotionEvent;
@@ -47,7 +46,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
-import com.android.systemui.shared.recents.model.Task;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
@@ -55,8 +53,6 @@
 public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
 
     private final ColorDrawable mBackgroundDrawable;
-    private final int mDividerWidth;
-    private final int mDividerHeight;
     private final int mIconTouchSize;
     private final boolean mIsRtl;
     private final int mTouchSlop;
@@ -74,9 +70,6 @@
     private LayoutTransition mLayoutTransition;
     private int mHotseatStartIndex;
     private int mHotseatEndIndex;
-    private View mHotseatRecentsDivider;
-    private int mRecentsStartIndex;
-    private int mRecentsEndIndex;
 
     // Delegate touches to the closest view if within mIconTouchSize.
     private boolean mDelegateTargeted;
@@ -105,8 +98,6 @@
 
         Resources resources = getResources();
         mBackgroundDrawable = (ColorDrawable) getBackground();
-        mDividerWidth = resources.getDimensionPixelSize(R.dimen.taskbar_divider_thickness);
-        mDividerHeight = resources.getDimensionPixelSize(R.dimen.taskbar_divider_height);
         mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
         mIsRtl = Utilities.isRtl(resources);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
@@ -119,18 +110,11 @@
         mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
     }
 
-    protected void init(int numHotseatIcons, int numRecentIcons) {
+    protected void init(int numHotseatIcons) {
         mHotseatStartIndex = 0;
         mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
 
-        int dividerIndex = mHotseatEndIndex + 1;
-        mHotseatRecentsDivider = addDivider(dividerIndex);
-
-        mRecentsStartIndex = dividerIndex + 1;
-        mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
-        updateRecentTasks(new Task[numRecentIcons]);
-
         mLayoutTransition = new LayoutTransition();
         addUpdateListenerForAllLayoutTransitions(() -> {
             if (getLayoutTransition() == mLayoutTransition) {
@@ -165,7 +149,6 @@
         endAllLayoutTransitionAnimators();
         setLayoutTransition(null);
         removeAllViews();
-        mHotseatRecentsDivider = null;
     }
 
     private void endAllLayoutTransitionAnimators() {
@@ -248,8 +231,6 @@
             }
             updateHotseatItemVisibility(hotseatView);
         }
-
-        updateHotseatRecentsDividerVisibility();
     }
 
     protected void updateHotseatItemsVisibility() {
@@ -273,95 +254,6 @@
         }
     }
 
-    private View addDivider(int dividerIndex) {
-        View divider = inflate(R.layout.taskbar_divider);
-        LayoutParams lp = new LayoutParams(mDividerWidth, mDividerHeight);
-        lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-        divider.setScaleX(mNonIconScale);
-        divider.setScaleY(mNonIconScale);
-        addView(divider, dividerIndex, lp);
-        return divider;
-    }
-
-    /**
-     * Inflates/binds the Recents items to show in the Taskbar given their Tasks.
-     */
-    protected void updateRecentTasks(Task[] tasks) {
-        for (int i = 0; i < tasks.length; i++) {
-            Task task = tasks[i];
-            int recentsIndex = mRecentsStartIndex + i;
-            View recentsView = getChildAt(recentsIndex);
-
-            // Inflate empty icon Views.
-            if (recentsView == null) {
-                BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon);
-                LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
-                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
-                recentsView = btv;
-                addView(recentsView, recentsIndex, lp);
-            }
-
-            // Apply the Task, or hide the view if there is none for a given index.
-            if (recentsView instanceof BubbleTextView && task != null) {
-                applyTaskToBubbleTextView((BubbleTextView) recentsView, task);
-                recentsView.setVisibility(VISIBLE);
-                recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
-                recentsView.setOnLongClickListener(
-                        mControllerCallbacks.getItemOnLongClickListener());
-            } else {
-                recentsView.setVisibility(GONE);
-                recentsView.setOnClickListener(null);
-                recentsView.setOnLongClickListener(null);
-            }
-        }
-
-        updateHotseatRecentsDividerVisibility();
-    }
-
-    private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) {
-        if (task.icon != null) {
-            Drawable icon = task.icon.getConstantState().newDrawable().mutate();
-            btv.applyIconAndLabel(icon, task.titleDescription);
-        }
-        btv.setTag(task);
-    }
-
-    protected void updateRecentTaskAtIndex(int taskIndex, Task task) {
-        View taskView = getChildAt(mRecentsStartIndex + taskIndex);
-        if (taskView instanceof BubbleTextView) {
-            applyTaskToBubbleTextView((BubbleTextView) taskView, task);
-        }
-    }
-
-    /**
-     * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in
-     * each, otherwise make it GONE.
-     */
-    private void updateHotseatRecentsDividerVisibility() {
-        if (mHotseatRecentsDivider == null) {
-            return;
-        }
-
-        boolean hasAtLeastOneHotseatItem = false;
-        for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
-            if (getChildAt(i).getVisibility() != GONE) {
-                hasAtLeastOneHotseatItem = true;
-                break;
-            }
-        }
-
-        boolean hasAtLeastOneRecentItem = false;
-        for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) {
-            if (getChildAt(i).getVisibility() != GONE) {
-                hasAtLeastOneRecentItem = true;
-                break;
-            }
-        }
-
-        mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem
-                ? VISIBLE : GONE);
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         boolean handled = delegateTouchIfNecessary(event);
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index b336d2f..4c10822 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -149,6 +149,7 @@
 
         SharedPreferences prefs = getPrefs(mContext);
         StatsLogManager.LauncherEvent gridSizeChangedEvent = null;
+        // TODO(b/184981523): This doesn't work for 2-panel grid, which has 6 hotseat icons
         switch (prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1)) {
             case 5:
                 gridSizeChangedEvent = LAUNCHER_GRID_SIZE_5;
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 381b0fe..699c2e3 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -175,6 +175,9 @@
 
         <!-- numAllAppsColumns defaults to GridDisplayOption.numColumns, if not specified -->
         <attr name="numAllAppsColumns" format="integer" />
+
+        <!-- numShownHotseatIcons defaults to GridDisplayOption.numHotseatIcons, if not specified -->
+        <attr name="numShownHotseatIcons" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index adf720f..34a8025 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -127,7 +127,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, mIdp.numDatabaseHotseatIcons,
                 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/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index 8e00dcb..d544a0b 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -64,7 +64,7 @@
                 mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
@@ -81,7 +81,7 @@
                 mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index cca333c..8e49fae 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -120,7 +120,7 @@
         };
         mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
 
-        mIdp.numHotseatIcons = 4;
+        mIdp.numDatabaseHotseatIcons = 4;
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
@@ -128,16 +128,16 @@
                 srcHotseatItems.length);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
                 LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numHotseatIcons);
+                mIdp.numDatabaseHotseatIcons);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
         task.migrate();
 
         // Check hotseat items
         Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
                 new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
                 "container=" + CONTAINER_HOTSEAT, null, null, null);
-        assertEquals(c.getCount(), mIdp.numHotseatIcons);
+        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
         c.moveToNext();
@@ -186,5 +186,101 @@
         assertTrue(c.getString(intentIndex).contains(testPackage8));
         assertEquals(c.getInt(cellXIndex), 0);
         assertEquals(c.getInt(cellYIndex), 2);
+
+        c.close();
+    }
+
+    @Test
+    public void migrateToLargerHotseat() {
+        int[] srcHotseatItems = {
+                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+        };
+
+        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
+        mIdp.numDatabaseHotseatIcons = 6;
+        mIdp.numColumns = 4;
+        mIdp.numRows = 4;
+        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
+                numSrcDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // Check hotseat items
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
+                "container=" + CONTAINER_HOTSEAT, null, null, null);
+        assertEquals(c.getCount(), numSrcDatabaseHotseatIcons);
+        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 0);
+        assertTrue(c.getString(intentIndex).contains(testPackage1));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 1);
+        assertTrue(c.getString(intentIndex).contains(testPackage2));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 2);
+        assertTrue(c.getString(intentIndex).contains(testPackage3));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
+    }
+
+    @Test
+    public void migrateFromLargerHotseat() {
+        int[] srcHotseatItems = {
+                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                -1,
+                mModelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI),
+        };
+
+        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
+        mIdp.numDatabaseHotseatIcons = 4;
+        mIdp.numColumns = 4;
+        mIdp.numRows = 4;
+        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
+                numSrcDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // Check hotseat items
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
+                "container=" + CONTAINER_HOTSEAT, null, null, null);
+        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
+        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 0);
+        assertTrue(c.getString(intentIndex).contains(testPackage1));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 1);
+        assertTrue(c.getString(intentIndex).contains(testPackage2));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 2);
+        assertTrue(c.getString(intentIndex).contains(testPackage3));
+        c.moveToNext();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
     }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index fb08c56..800311a 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -160,7 +160,7 @@
     public void checkItemPlacement_outsideBounds() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Item outside screen bounds are not placed
         assertFalse(mLoaderCursor.checkItemPlacement(
@@ -171,7 +171,7 @@
     public void checkItemPlacement_overlappingItems() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Overlapping mItems are not placed
         assertTrue(mLoaderCursor.checkItemPlacement(
@@ -197,7 +197,7 @@
     public void checkItemPlacement_hotseat() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Hotseat mItems are only placed based on screenId
         assertTrue(mLoaderCursor.checkItemPlacement(
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 849f98b..846e201 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -355,7 +355,7 @@
             throws Exception {
         Context context = RuntimeEnvironment.application;
         InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
-        idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
+        idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
         idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
 
         Settings.Secure.putString(context.getContentResolver(),
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index f61bc05..5b655a4 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -89,7 +89,7 @@
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
-                grid.numColumns, grid.numRows, grid.numHotseatIcons);
+                grid.numColumns, grid.numRows, grid.numDatabaseHotseatIcons);
         int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
 
         // Try with only grid size
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 35b0f27..985a445 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -137,6 +137,7 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
+    public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
@@ -293,6 +294,7 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
+        numShownHotseatIcons = inv.numShownHotseatIcons;
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -735,7 +737,7 @@
             // for this, we pad the left and right of the hotseat with half of the difference of a
             // workspace cell vs a hotseat cell.
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
-            float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
+            float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
             mHotseatPadding.set(
                     hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 5429806..da41200 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -92,9 +92,9 @@
         mHasVerticalHotseat = hasVerticalHotseat;
         InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
         if (hasVerticalHotseat) {
-            setGridSize(1, idp.numHotseatIcons);
+            setGridSize(1, idp.numShownHotseatIcons);
         } else {
-            setGridSize(idp.numHotseatIcons, 1);
+            setGridSize(idp.numShownHotseatIcons, 1);
         }
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 5612daf..f198777 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -122,7 +122,14 @@
     /**
      * Number of icons inside the hotseat area.
      */
-    public int numHotseatIcons;
+    protected int numShownHotseatIcons;
+
+    /**
+     * Number of icons inside the hotseat area that is stored in the database. This is greater than
+     * or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
+     * sizes that share the same DB.
+     */
+    public int numDatabaseHotseatIcons;
 
     /**
      * Number of columns in the all apps list.
@@ -165,7 +172,8 @@
         iconBitmapSize = p.iconBitmapSize;
         iconTextSize = p.iconTextSize;
         landscapeIconTextSize = p.landscapeIconTextSize;
-        numHotseatIcons = p.numHotseatIcons;
+        numShownHotseatIcons = p.numShownHotseatIcons;
+        numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
         isScalable = p.isScalable;
         devicePaddingId = p.devicePaddingId;
@@ -190,7 +198,7 @@
             Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
         }
         Utilities.getPrefs(context).edit()
-                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numHotseatIcons)
+                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numDatabaseHotseatIcons)
                 .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
                 .apply();
 
@@ -229,6 +237,7 @@
                 .add(myDisplayOption);
         result.iconSize = defaultDisplayOption.iconSize;
         result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
+        result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons;
         if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
             result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
             result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
@@ -279,7 +288,7 @@
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
-        numHotseatIcons = closestProfile.numHotseatIcons;
+        numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons;
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -301,6 +310,7 @@
         minCellHeight = displayOption.minCellHeight;
         minCellWidth = displayOption.minCellWidth;
         borderSpacing = displayOption.borderSpacing;
+        numShownHotseatIcons = Math.round(displayOption.numShownHotseatIcons);
         numAllAppsColumns = Math.round(displayOption.numAllAppsColumns);
 
         if (Utilities.isGridOptionsEnabled(context)) {
@@ -391,7 +401,7 @@
                 numColumns != oldProfile.numColumns ||
                 numFolderColumns != oldProfile.numFolderColumns ||
                 numFolderRows != oldProfile.numFolderRows ||
-                numHotseatIcons != oldProfile.numHotseatIcons) {
+                numDatabaseHotseatIcons != oldProfile.numDatabaseHotseatIcons) {
             changeFlags |= CHANGE_FLAG_GRID;
         }
 
@@ -619,7 +629,7 @@
         private final int numFolderRows;
         private final int numFolderColumns;
 
-        private final int numHotseatIcons;
+        private final int numDatabaseHotseatIcons;
 
         private final String dbFile;
 
@@ -645,7 +655,7 @@
                     R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
-            numHotseatIcons = a.getInt(
+            numDatabaseHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
@@ -673,6 +683,7 @@
         private final float minHeightDps;
         private final boolean canBeDefault;
 
+        private float numShownHotseatIcons;
         private float numAllAppsColumns;
         private float minCellHeight;
         private float minCellWidth;
@@ -695,6 +706,8 @@
             minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
             canBeDefault = a.getBoolean(
                     R.styleable.ProfileDisplayOption_canBeDefault, false);
+            numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons,
+                    grid.numDatabaseHotseatIcons);
             numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
                     grid.numColumns);
 
@@ -725,6 +738,7 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
+            numShownHotseatIcons = 0;
             numAllAppsColumns = 0;
             minCellHeight = 0;
             minCellWidth = 0;
@@ -732,6 +746,7 @@
         }
 
         private DisplayOption multiply(float w) {
+            numShownHotseatIcons *= w;
             numAllAppsColumns *= w;
             iconSize *= w;
             landscapeIconSize *= w;
@@ -746,6 +761,7 @@
         }
 
         private DisplayOption add(DisplayOption p) {
+            numShownHotseatIcons += p.numShownHotseatIcons;
             numAllAppsColumns += p.numAllAppsColumns;
             iconSize += p.iconSize;
             landscapeIconSize += p.landscapeIconSize;
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 2ec5e90..39b16fd 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -618,7 +618,7 @@
                 .appendQueryParameter("version", "1")
                 .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
                 .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
-                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
+                .appendQueryParameter("hotseatSize", Integer.toString(grid.numDatabaseHotseatIcons))
                 .build();
     }
 
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index a8185d6..2491217 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -106,7 +106,7 @@
                 - mAppsView.getActiveRecyclerView().getPaddingRight();
 
         int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.cellLayoutBorderSpacingPx,
-                dp.inv.numHotseatIcons);
+                dp.numShownHotseatIcons);
         int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
         int iconPadding = cellWidth - iconVisibleSize;
 
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 5e9b179..31764c5 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -467,7 +467,7 @@
                 }
             }
             IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
-                    mIdp.numHotseatIcons);
+                    mDp.numShownHotseatIcons);
             List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
                     ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
             int count = Math.min(ranks.size(), predictions.size());
@@ -484,7 +484,7 @@
             }
         } else {
             // Add hotseat icons
-            for (int i = 0; i < mIdp.numHotseatIcons; i++) {
+            for (int i = 0; i < mDp.numShownHotseatIcons; i++) {
                 WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
                 info.container = Favorites.CONTAINER_HOTSEAT;
                 info.screenId = i;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 804e72e..6a75e62 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -893,7 +893,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -928,7 +928,7 @@
                 .getBinder(Settings.EXTRA_VALUE)) {
 
             int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
-                    idp.numHotseatIcons);
+                    idp.numDatabaseHotseatIcons);
             Point sourceSize = parsePoint(prefs.getString(
                     KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
 
@@ -948,10 +948,10 @@
 
             HashSet<String> validPackages = getValidPackages(context);
             // Hotseat.
-            if (srcHotseatCount != idp.numHotseatIcons
+            if (srcHotseatCount != idp.numDatabaseHotseatIcons
                     && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
                             migrateForPreview, srcHotseatCount,
-                            idp.numHotseatIcons).migrateHotseat()) {
+                            idp.numDatabaseHotseatIcons).migrateHotseat()) {
                 dbChanged = true;
             }
 
@@ -991,7 +991,7 @@
                 // Save current configuration, so that the migration does not run again.
                 prefs.edit()
                         .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index c6c0791..cee9304 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -110,7 +110,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -148,7 +148,8 @@
         SharedPreferences prefs = Utilities.getPrefs(context);
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
         HashSet<String> validPackages = getValidPackages(context);
-        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
+        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+                idp.numDatabaseHotseatIcons);
 
         if (migrateForPreview) {
             if (!LauncherSettings.Settings.call(
@@ -177,11 +178,11 @@
             DbReader destReader = new DbReader(t.getDb(),
                     migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
                             : LauncherSettings.Favorites.TABLE_NAME,
-                    context, validPackages, idp.numHotseatIcons);
+                    context, validPackages, idp.numDatabaseHotseatIcons);
 
             Point targetSize = new Point(idp.numColumns, idp.numRows);
             GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
-                    srcReader, destReader, idp.numHotseatIcons, targetSize);
+                    srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
             task.migrate();
 
             if (!migrateForPreview) {
@@ -202,7 +203,7 @@
                 // Save current configuration, so that the migration does not run again.
                 prefs.edit()
                         .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 19d9af9..897b02e 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -410,10 +410,10 @@
             final GridOccupancy hotseatOccupancy =
                     occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
-            if (item.screenId >= mIDP.numHotseatIcons) {
+            if (item.screenId >= mIDP.numDatabaseHotseatIcons) {
                 Log.e(TAG, "Error loading shortcut " + item
                         + " into hotseat position " + item.screenId
-                        + ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+                        + ", position out of bounds: (0 to " + (mIDP.numDatabaseHotseatIcons - 1)
                         + ")");
                 return false;
             }
@@ -429,7 +429,7 @@
                     return true;
                 }
             } else {
-                final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
+                final GridOccupancy occupancy = new GridOccupancy(mIDP.numDatabaseHotseatIcons, 1);
                 occupancy.cells[item.screenId][0] = true;
                 occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
                 return true;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 312435d..080ce20 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -91,7 +91,7 @@
         // in the hotseat
         if (container == Favorites.CONTAINER_HOTSEAT) {
             item.screenId = mHasVerticalHotseat
-                    ? LauncherAppState.getIDP(mContext).numHotseatIcons - cellY - 1 : cellX;
+                    ? LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons - cellY - 1 : cellX;
         } else {
             item.screenId = screenId;
         }
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index a5462a6..c9af2fe 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -284,8 +284,9 @@
             insertOperations.clear();
         }
 
-        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
-        int myHotseatCount = LauncherAppState.getIDP(mContext).numHotseatIcons;
+        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask
+                .removeBrokenHotseatItems(mContext);
+        int myHotseatCount = LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons;
         if (hotseatItems.size() < myHotseatCount) {
             // Insufficient hotseat items. Add a few more.
             HotseatParserCallback parserCallback = new HotseatParserCallback(
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 53183bf..223f4f1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -103,7 +103,7 @@
      */
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+        new GridBackupTable(context, db, idp.numDatabaseHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
 
@@ -111,7 +111,7 @@
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
             throws Exception {
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numDatabaseHotseatIcons,
                 idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
             int itemsDeleted = sanitizeDB(helper, db, backupManager);