Moving hotseat predictions to ModelDelegate

Bug: 160748731

Change-Id: I8db7856a17e0b6ca45b4d5ec3513f788f22db11a
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 397ea82..8f4ce43 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -27,8 +27,6 @@
 
   <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
 
-  <string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
-
   <string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
 
 </resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index a0016cb..68111d2 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -26,12 +26,10 @@
 import android.animation.ValueAnimator;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -40,14 +38,12 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
-import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.QuickstepOnboardingPrefs;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.views.OverviewActionsView;
@@ -73,7 +69,6 @@
                     Float.intBitsToFloat(arg1), arg2 != 0);
 
     private OverviewActionsView mActionsView;
-    protected HotseatPredictionController mHotseatPredictionController;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -222,11 +217,6 @@
     }
 
     @Override
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
-        return new QuickstepOnboardingPrefs(this, sharedPrefs);
-    }
-
-    @Override
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
         QuickstepAppTransitionManagerImpl appTransitionManager =
                 (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
@@ -314,13 +304,6 @@
                 Stream.of(WellbeingModel.SHORTCUT_FACTORY));
     }
 
-    /**
-     * Returns Prediction controller for hybrid hotseat
-     */
-    public HotseatPredictionController getHotseatPredictionController() {
-        return mHotseatPredictionController;
-    }
-
     public void setHintUserWillBeActive() {
         addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
     }
diff --git a/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
deleted file mode 100644
index d200868..0000000
--- a/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2019 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.appprediction;
-
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.util.ComponentKey;
-
-public class ComponentKeyMapper {
-
-    protected final ComponentKey componentKey;
-    private final DynamicItemCache mCache;
-
-    public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) {
-        componentKey = key;
-        mCache = cache;
-    }
-
-    public String getPackage() {
-        return componentKey.componentName.getPackageName();
-    }
-
-    public String getComponentClass() {
-        return componentKey.componentName.getClassName();
-    }
-
-    public ComponentKey getComponentKey() {
-        return componentKey;
-    }
-
-    @Override
-    public String toString() {
-        return componentKey.toString();
-    }
-
-    public ItemInfoWithIcon getApp(AllAppsStore store) {
-        AppInfo item = store.getApp(componentKey);
-        if (item != null) {
-            return item;
-        } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
-            return mCache.getInstantApp(componentKey.componentName.getPackageName());
-        } else {
-            return mCache.getShortcutInfo(componentKey);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java
deleted file mode 100644
index ab96b1340..0000000
--- a/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/**
- * Copyright (C) 2019 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.appprediction;
-
-import static android.content.pm.PackageManager.MATCH_INSTANT;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utility class which loads and caches predicted items like instant apps and shortcuts, before
- * they can be displayed on the UI
- */
-public class DynamicItemCache {
-
-    private static final String TAG = "DynamicItemCache";
-    private static final boolean DEBUG = false;
-    private static final String DEFAULT_URL = "default-url";
-
-    private static final int BG_MSG_LOAD_SHORTCUTS = 1;
-    private static final int BG_MSG_LOAD_INSTANT_APPS = 2;
-
-    private static final int UI_MSG_UPDATE_SHORTCUTS = 1;
-    private static final int UI_MSG_UPDATE_INSTANT_APPS = 2;
-
-    private final Context mContext;
-    private final Handler mWorker;
-    private final Handler mUiHandler;
-    private final InstantAppResolver mInstantAppResolver;
-    private final Runnable mOnUpdateCallback;
-    private final IconCache mIconCache;
-
-    private final Map<ComponentKey, WorkspaceItemInfo> mShortcuts;
-    private final Map<String, InstantAppItemInfo> mInstantApps;
-
-    public DynamicItemCache(Context context, Runnable onUpdateCallback) {
-        mContext = context;
-        mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
-        mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
-        mInstantAppResolver = InstantAppResolver.newInstance(context);
-        mOnUpdateCallback = onUpdateCallback;
-        mIconCache = LauncherAppState.getInstance(mContext).getIconCache();
-
-        mShortcuts = new HashMap<>();
-        mInstantApps = new HashMap<>();
-    }
-
-    public void cacheItems(List<ShortcutKey> shortcutKeys, List<String> pkgNames) {
-        if (!shortcutKeys.isEmpty()) {
-            mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
-            Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
-        }
-        if (!pkgNames.isEmpty()) {
-            mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
-            Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
-        }
-    }
-
-    private boolean handleWorkerMessage(Message msg) {
-        switch (msg.what) {
-            case BG_MSG_LOAD_SHORTCUTS: {
-                List<ShortcutKey> shortcutKeys = msg.obj != null ?
-                        (List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
-                Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
-                for (ShortcutKey shortcutKey : shortcutKeys) {
-                    WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
-                    if (workspaceItemInfo != null) {
-                        shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
-                    }
-                }
-                Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
-                        .sendToTarget();
-                return true;
-            }
-            case BG_MSG_LOAD_INSTANT_APPS: {
-                List<String> pkgNames = msg.obj != null ?
-                        (List<String>) msg.obj : Collections.EMPTY_LIST;
-                List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
-                for (String pkgName : pkgNames) {
-                    InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
-                    if (instantAppItemInfo != null) {
-                        instantAppItemInfos.add(instantAppItemInfo);
-                    }
-                }
-                Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
-                        .sendToTarget();
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private boolean handleUiMessage(Message msg) {
-        switch (msg.what) {
-            case UI_MSG_UPDATE_SHORTCUTS: {
-                mShortcuts.clear();
-                mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
-                mOnUpdateCallback.run();
-                return true;
-            }
-            case UI_MSG_UPDATE_INSTANT_APPS: {
-                List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
-                mInstantApps.clear();
-                for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
-                    mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
-                            instantAppItemInfo);
-                }
-                mOnUpdateCallback.run();
-                if (DEBUG) {
-                    Log.d(TAG, String.format("Cache size: %d, Cache: %s",
-                            mInstantApps.size(), mInstantApps.toString()));
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @WorkerThread
-    private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
-        List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
-        if (!details.isEmpty()) {
-            WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
-            mIconCache.getShortcutIcon(si, details.get(0));
-            return si;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "No shortcut found: " + shortcutKey.toString());
-        }
-        return null;
-    }
-
-    private InstantAppItemInfo loadInstantApp(String pkgName) {
-        PackageManager pm = mContext.getPackageManager();
-
-        try {
-            ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
-            if (!mInstantAppResolver.isInstantApp(ai)) {
-                return null;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-
-        String url = retrieveDefaultUrl(pkgName, pm);
-        if (url == null) {
-            Log.w(TAG, "no default-url available for pkg " + pkgName);
-            return null;
-        }
-
-        Intent intent = new Intent(Intent.ACTION_VIEW)
-                .addCategory(Intent.CATEGORY_BROWSABLE)
-                .setData(Uri.parse(url));
-        InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
-        IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
-        iconCache.getTitleAndIcon(info, false);
-        if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
-            return null;
-        }
-        return info;
-    }
-
-    @Nullable
-    public static String retrieveDefaultUrl(String pkgName, PackageManager pm) {
-        Intent mainIntent = new Intent().setAction(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER).setPackage(pkgName);
-        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
-                mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
-        String url = null;
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            if (resolveInfo.activityInfo.metaData != null
-                    && resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
-                url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
-            }
-        }
-        return url;
-    }
-
-    @UiThread
-    public InstantAppItemInfo getInstantApp(String pkgName) {
-        return mInstantApps.get(pkgName);
-    }
-
-    @MainThread
-    public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
-        return mShortcuts.get(key);
-    }
-
-    /**
-     * requests and caches icons for app targets
-     */
-    public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
-            AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
-        List<String> instantAppsToLoad = new ArrayList<>();
-        List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
-        int total = componentKeyMappers.size();
-        for (int i = 0, count = 0; i < total && count < itemCount; i++) {
-            ComponentKeyMapper mapper = componentKeyMappers.get(i);
-            // Update instant apps
-            if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
-                instantAppsToLoad.add(mapper.getPackage());
-                count++;
-            } else if (mapper.getComponentKey() instanceof ShortcutKey) {
-                shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
-                count++;
-            } else {
-                // Reload high res icon
-                AppInfo info = (AppInfo) mapper.getApp(appsStore);
-                if (info != null) {
-                    if (info.usingLowResIcon()) {
-                        mIconCache.updateIconInBackground(callback, info);
-                    }
-                    count++;
-                }
-            }
-        }
-        cacheItems(shortcutsToLoad, instantAppsToLoad);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 8ebf125..4451e7a 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -47,35 +47,27 @@
  */
 public class HotseatEduController {
 
-    public static final String HOTSEAT_EDU_ACTION =
-            "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
     public static final String SETTINGS_ACTION =
             "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
 
     private final Launcher mLauncher;
     private final Hotseat mHotseat;
-    private HotseatRestoreHelper mRestoreHelper;
     private List<WorkspaceItemInfo> mPredictedApps;
     private HotseatEduDialog mActiveDialog;
 
     private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
     private IntArray mNewScreens = null;
-    private Runnable mOnOnboardingComplete;
 
-    HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) {
+    HotseatEduController(Launcher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
-        mRestoreHelper = restoreHelper;
-        mOnOnboardingComplete = runnable;
     }
 
     /**
      * Checks what type of migration should be used and migrates hotseat
      */
     void migrate() {
-        if (mRestoreHelper != null) {
-            mRestoreHelper.createBackup();
-        }
+        HotseatRestoreHelper.createBackup(mLauncher);
         if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
             migrateToFolder();
         } else {
@@ -227,7 +219,7 @@
     }
 
     void finishOnboarding() {
-        mOnOnboardingComplete.run();
+        mLauncher.getModel().onWorkspaceUiChanged();
     }
 
     void showDimissTip() {
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index b94e633..6fe16c9 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -15,23 +15,17 @@
  */
 package com.android.launcher3.hybridhotseat;
 
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
 import android.content.ComponentName;
 import android.os.Process;
-import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 import android.view.ViewGroup;
@@ -44,77 +38,51 @@
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.appprediction.ComponentKeyMapper;
-import com.android.launcher3.appprediction.DynamicItemCache;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
 import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.Snackbar;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
+import java.util.stream.Collectors;
 
 /**
  * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
  * pinning of predicted apps and manages replacement of predicted apps with user drag.
  */
 public class HotseatPredictionController implements DragController.DragListener,
-        View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
-        InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
-        IconCache.ItemInfoUpdateReceiver, DragSource {
+        SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
+        DragSource {
 
-    private static final String TAG = "PredictiveHotseat";
-    private static final boolean DEBUG = false;
-
-    private static final String PREDICTION_CLIENT = "hotseat";
-    private DropTarget.DragObject mDragObject;
     private int mHotSeatItemsCount;
-    private int mPredictedSpotsCount = 0;
 
     private Launcher mLauncher;
     private final Hotseat mHotseat;
 
-    private final HotseatRestoreHelper mRestoreHelper;
+    private List<ItemInfo> mPredictedItems = Collections.emptyList();
 
-    private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
-
-    private DynamicItemCache mDynamicItemCache;
-
-    private final HotseatPredictionModel mPredictionModel;
-    private AppPredictor mAppPredictor;
-    private AllAppsStore mAllAppsStore;
     private AnimatorSet mIconRemoveAnimators;
     private boolean mUIUpdatePaused = false;
-    private boolean mIsDestroyed = false;
-
+    private boolean mDragInProgress = false;
 
     private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
 
@@ -130,26 +98,23 @@
             mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
             return true;
         }
+
         // Start the drag
-        mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
+        // Use a new itemInfo so that the original predicted item is stable
+        WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag());
+        v.setVisibility(View.INVISIBLE);
+        mLauncher.getWorkspace().beginDragShared(
+                v, null, this, dragItem, new DragPreviewProvider(v), new DragOptions());
         return true;
     };
 
     public HotseatPredictionController(Launcher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
-        mAllAppsStore = mLauncher.getAppsView().getAppsStore();
-        LauncherAppState appState = LauncherAppState.getInstance(launcher);
-        mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
-        mAllAppsStore.addUpdateListener(this);
-        mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
         mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+        mLauncher.getDragController().addDragListener(this);
+
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
-        mHotseat.addOnAttachStateChangeListener(this);
-        mRestoreHelper = new HotseatRestoreHelper(mLauncher);
-        if (mHotseat.isAttachedToWindow()) {
-            onViewAttachedToWindow(mHotseat);
-        }
     }
 
     /**
@@ -157,7 +122,7 @@
      */
     public void showEdu() {
         mLauncher.getStateManager().goToState(NORMAL, true, () -> {
-            if (mComponentKeyMappers.isEmpty()) {
+            if (mPredictedItems.isEmpty()) {
                 // launcher has empty predictions set
                 Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
                         R.string.hotseat_prediction_settings, null,
@@ -165,10 +130,10 @@
             } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
                 showDiscoveryTip();
             } else {
-                HotseatEduController eduController = new HotseatEduController(mLauncher,
-                        mRestoreHelper,
-                        this::createPredictor);
-                eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
+                HotseatEduController eduController = new HotseatEduController(mLauncher);
+                eduController.setPredictedApps(mPredictedItems.stream()
+                        .map(i -> (WorkspaceItemInfo) i)
+                        .collect(Collectors.toList()));
                 eduController.showEdu();
             }
         });
@@ -192,17 +157,7 @@
      * Returns if hotseat client has predictions
      */
     public boolean hasPredictions() {
-        return !mComponentKeyMappers.isEmpty();
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View view) {
-        mLauncher.getDragController().addDragListener(this);
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View view) {
-        mLauncher.getDragController().removeDragListener(this);
+        return !mPredictedItems.isEmpty();
     }
 
     private void fillGapsWithPrediction() {
@@ -210,15 +165,10 @@
     }
 
     private void fillGapsWithPrediction(boolean animate, Runnable callback) {
-        if (mUIUpdatePaused || mDragObject != null) {
+        if (mUIUpdatePaused || mDragInProgress) {
             return;
         }
-        List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
-        if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
-            // Safely ignore update as AppsList is not ready yet. This will called again once
-            // apps are ready (HotseatPredictionController#onAppsUpdated)
-            return;
-        }
+
         int predictionIndex = 0;
         ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
         // make sure predicted icon removal and filling predictions don't step on each other
@@ -240,14 +190,15 @@
             if (child != null && !isPredictedIcon(child)) {
                 continue;
             }
-            if (predictedApps.size() <= predictionIndex) {
+            if (mPredictedItems.size() <= predictionIndex) {
                 // Remove predicted apps from the past
                 if (isPredictedIcon(child)) {
                     mHotseat.removeView(child);
                 }
                 continue;
             }
-            WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
+            WorkspaceItemInfo predictedItem =
+                    (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
             if (isPredictedIcon(child) && child.isEnabled()) {
                 PredictedAppIcon icon = (PredictedAppIcon) child;
                 icon.applyFromWorkspaceItem(predictedItem);
@@ -257,7 +208,6 @@
             }
             preparePredictionInfo(predictedItem, rank);
         }
-        mPredictedSpotsCount = predictionIndex;
         bindItems(newItems, animate, callback);
     }
 
@@ -285,13 +235,7 @@
      * Unregisters callbacks and frees resources
      */
     public void destroy() {
-        mIsDestroyed = true;
-        mAllAppsStore.removeUpdateListener(this);
         mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
-        mHotseat.removeOnAttachStateChangeListener(this);
-        if (mAppPredictor != null) {
-            mAppPredictor.destroy();
-        }
     }
 
     /**
@@ -305,97 +249,14 @@
     }
 
     /**
-     * Creates App Predictor with all the current apps pinned on the hotseat
+     * Sets or updates the predicted items
      */
-    public void createPredictor() {
-        AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
-        if (apm == null) {
-            return;
+    public void setPredictedItems(FixedContainerItems items) {
+        mPredictedItems = items.items;
+        if (mPredictedItems.isEmpty()) {
+            HotseatRestoreHelper.restoreBackup(mLauncher);
         }
-        if (mAppPredictor != null) {
-            mAppPredictor.destroy();
-            mAppPredictor = null;
-        }
-        WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
-
-
-        mPredictionModel.createBundle(bundle -> {
-            if (mIsDestroyed) return;
-            mAppPredictor = apm.createAppPredictionSession(
-                    new AppPredictionContext.Builder(mLauncher)
-                            .setUiSurface(PREDICTION_CLIENT)
-                            .setPredictedTargetCount(mHotSeatItemsCount)
-                            .setExtras(bundle)
-                            .build());
-            mAppPredictor.registerPredictionUpdates(
-                    mLauncher.getApplicationContext().getMainExecutor(),
-                    list -> {
-                        if (controllerRef.get() != null) {
-                            controllerRef.get().setPredictedApps(list);
-                        }
-                    });
-            mAppPredictor.requestPredictionUpdate();
-        });
-        setPauseUIUpdate(false);
-    }
-
-    /**
-     * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
-     */
-    public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
-        if (hasPredictions() && mAppPredictor != null) {
-            mAppPredictor.requestPredictionUpdate();
-            fillGapsWithPrediction();
-            return;
-        }
-        int count = Math.min(ranks.size(), apps.size());
-        List<WorkspaceItemInfo> items = new ArrayList<>(count);
-        for (int i = 0; i < count; i++) {
-            WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
-            ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
-            preparePredictionInfo(item, ranks.get(i));
-            items.add(item);
-
-            mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
-        }
-        updateDependencies();
-        bindItems(items, false, null);
-    }
-
-    private void setPredictedApps(List<AppTarget> appTargets) {
-        mComponentKeyMappers.clear();
-        if (appTargets.isEmpty()) {
-            mRestoreHelper.restoreBackup();
-        }
-        StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
-        ArrayList<ComponentKey> componentKeys = new ArrayList<>();
-        for (AppTarget appTarget : appTargets) {
-            ComponentKey key;
-            if (appTarget.getShortcutInfo() != null) {
-                key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
-            } else {
-                key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
-                        appTarget.getClassName()), appTarget.getUser());
-            }
-            componentKeys.add(key);
-            predictionLog.append(key.toString());
-            predictionLog.append(",rank:");
-            predictionLog.append(appTarget.getRank());
-            predictionLog.append("\n");
-            mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
-        }
-        predictionLog.append("]");
-        if (Utilities.IS_DEBUG_DEVICE) {
-            HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString());
-        }
-        updateDependencies();
         fillGapsWithPrediction();
-        mPredictionModel.cachePredictionComponentKeys(componentKeys);
-    }
-
-    private void updateDependencies() {
-        mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
-                mHotSeatItemsCount);
     }
 
     /**
@@ -414,42 +275,9 @@
                 workspaceItemInfo.cellX, workspaceItemInfo.cellY);
         ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
         icon.pin(workspaceItemInfo);
-        AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
-        if (appTarget != null) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                    AppTargetEvent.ACTION_PIN, workspaceItemInfo));
-        }
-    }
-
-    private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
-            List<ComponentKeyMapper> components) {
-        AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
-        if (allAppsStore.getApps().length == 0) {
-            return Collections.emptyList();
-        }
-
-        List<WorkspaceItemInfo> predictedApps = new ArrayList<>();
-        for (ComponentKeyMapper mapper : components) {
-            ItemInfoWithIcon info = mapper.getApp(allAppsStore);
-            if (info instanceof AppInfo) {
-                WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
-                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-                predictedApps.add(predictedApp);
-            } else if (info instanceof WorkspaceItemInfo) {
-                WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((WorkspaceItemInfo) info);
-                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-                predictedApps.add(predictedApp);
-            } else {
-                if (DEBUG) {
-                    Log.e(TAG, "Predicted app not found: " + mapper);
-                }
-            }
-            // Stop at the number of hotseat items
-            if (predictedApps.size() == mHotSeatItemsCount) {
-                break;
-            }
-        }
-        return predictedApps;
+        mLauncher.getStatsLogManager().logger()
+                .withItemInfo(workspaceItemInfo)
+                .log(LAUNCHER_HOTSEAT_PREDICTION_PINNED);
     }
 
     private List<PredictedAppIcon> getPredictedIcons() {
@@ -465,7 +293,7 @@
     }
 
     private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
-            ItemInfo draggedInfo) {
+            DropTarget.DragObject dragObject) {
         if (mIconRemoveAnimators != null) {
             mIconRemoveAnimators.end();
         }
@@ -475,7 +303,7 @@
             if (!icon.isEnabled()) {
                 continue;
             }
-            if (icon.getTag().equals(draggedInfo)) {
+            if (dragObject.dragSource == this && icon.equals(dragObject.originalView)) {
                 mHotseat.removeView(icon);
                 continue;
             }
@@ -497,84 +325,23 @@
         mIconRemoveAnimators.start();
     }
 
-    private void notifyItemAction(AppTargetEvent event) {
-        if (mAppPredictor != null) {
-            mAppPredictor.notifyAppTargetEvent(event);
-        }
-    }
-
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
-        removePredictedApps(mOutlineDrawings, dragObject.dragInfo);
-        mDragObject = dragObject;
+        removePredictedApps(mOutlineDrawings, dragObject);
         if (mOutlineDrawings.isEmpty()) return;
         for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
             mHotseat.addDelegatedCellDrawing(outlineDrawing);
         }
+        mDragInProgress = true;
         mHotseat.invalidate();
     }
 
-    /**
-     * Unpins pinned app when it's converted into a folder
-     */
-    public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
-        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
-        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
-        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
-                    AppTargetEvent.ACTION_PIN, folderInfo));
-        }
-        // using folder info with isTrackedForPrediction as itemInfo.container is already changed
-        // to folder by this point
-        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
-                    AppTargetEvent.ACTION_UNPIN, folderInfo
-            ));
-        }
-    }
-
-    /**
-     * Pins workspace item created when all folder items are removed but one
-     */
-    public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
-        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
-        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
-        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
-                    AppTargetEvent.ACTION_UNPIN, folderInfo));
-        }
-        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
-                    AppTargetEvent.ACTION_PIN, itemInfo));
-        }
-    }
-
     @Override
     public void onDragEnd() {
-        if (mDragObject == null) {
-            return;
-        }
-
-        ItemInfo dragInfo = mDragObject.dragInfo;
-        if (mDragObject.isMoved()) {
-            AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
-            //always send pin event first to prevent AiAi from predicting an item moved within
-            // the same page
-            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
-                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                        AppTargetEvent.ACTION_PIN, dragInfo));
-            }
-            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
-                    mDragObject.originalDragInfo)) {
-                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                        AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
-            }
-        }
-        mDragObject = null;
+        mDragInProgress = false;
         fillGapsWithPrediction(true, this::removeOutlineDrawings);
     }
 
-
     @Nullable
     @Override
     public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
@@ -604,19 +371,7 @@
 
     @Override
     public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
-            this.mHotSeatItemsCount = profile.numHotseatIcons;
-            createPredictor();
-        }
-    }
-
-    @Override
-    public void onAppsUpdated() {
-        fillGapsWithPrediction();
-    }
-
-    @Override
-    public void reapplyItemInfo(ItemInfoWithIcon info) {
+        this.mHotSeatItemsCount = profile.numHotseatIcons;
     }
 
     @Override
@@ -643,17 +398,20 @@
                             + ",launchLocation:" + itemInfo.container);
         }
 
-        if (itemInfo.getTargetComponent() == null || itemInfo.user == null) {
+
+        ComponentName targetCN = itemInfo.getTargetComponent();
+        if (targetCN == null) {
             return;
         }
-
-        final ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
-        final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
-        OptionalInt rank = IntStream.range(0, predictedApps.size())
-                .filter(index -> key.equals(predictedApps.get(index).getComponentKey()))
-                .findFirst();
-        if (!rank.isPresent()) {
+        int rank = -1;
+        for (int i = mPredictedItems.size() - 1; i >= 0; i--) {
+            ItemInfo info = mPredictedItems.get(i);
+            if (targetCN.equals(info.getTargetComponent()) && itemInfo.user.equals(info.user)) {
+                rank = i;
+                break;
+            }
+        }
+        if (rank < 0) {
             return;
         }
 
@@ -666,11 +424,11 @@
         PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
         containerBuilder.setCardinality(cardinality);
         if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            containerBuilder.setIndex(rank.getAsInt());
+            containerBuilder.setIndex(rank);
         }
         mLauncher.getStatsLogManager().logger()
                 .withInstanceId(instanceId)
-                .withRank(rank.getAsInt())
+                .withRank(rank)
                 .withContainerInfo(ContainerInfo.newBuilder()
                         .setPredictedHotseatContainer(containerBuilder)
                         .build())
@@ -691,30 +449,6 @@
         }
     }
 
-    /**
-     * Fill in predicted_rank field based on app prediction.
-     * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
-     */
-    public static void encodeHotseatLayoutIntoPredictionRank(
-            @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
-        QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
-        if (launcher == null || launcher.getHotseatPredictionController() == null
-                || itemInfo.getTargetComponent() == null) {
-            return;
-        }
-        HotseatPredictionController controller = launcher.getHotseatPredictionController();
-
-        final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
-        final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
-        OptionalInt rank = IntStream.range(0, predictedApps.size())
-                .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
-                .findFirst();
-
-        target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100)
-                + (rank.isPresent() ? rank.getAsInt() + 1 : 0);
-    }
-
     private static boolean isPredictedIcon(View view) {
         return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
                 && ((WorkspaceItemInfo) view.getTag()).container
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 5a038d2..8f31c22 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3.hybridhotseat;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
@@ -24,13 +24,10 @@
 import android.content.Context;
 import android.os.Bundle;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.model.AllAppsList;
-import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.PredictionModel;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -38,55 +35,48 @@
 
 import java.util.ArrayList;
 import java.util.Locale;
-import java.util.function.Consumer;
 
 /**
  * Model helper for app predictions in workspace
  */
-public class HotseatPredictionModel extends PredictionModel {
+public class HotseatPredictionModel {
     private static final String APP_LOCATION_HOTSEAT = "hotseat";
     private static final String APP_LOCATION_WORKSPACE = "workspace";
 
     private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
     private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
 
-
-    public HotseatPredictionModel(Context context) { }
-
     /**
-     * Creates and returns bundle using workspace items and cached items
+     * Creates and returns bundle using workspace items
      */
-    public void createBundle(Consumer<Bundle> cb) {
-        LauncherAppState appState = LauncherAppState.getInstance(mContext);
-        appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
-            @Override
-            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                Bundle bundle = new Bundle();
-                ArrayList<AppTargetEvent> events = new ArrayList<>();
-                ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
-                workspaceItems.addAll(dataModel.appWidgets);
-                for (ItemInfo item : workspaceItems) {
-                    AppTarget target = getAppTargetFromInfo(item);
-                    if (target != null && !isTrackedForPrediction(item)) continue;
-                    events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
-                }
-                ArrayList<AppTarget> currentTargets = new ArrayList<>();
-                for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
-                    AppTarget target = getAppTargetFromInfo(itemInfo);
-                    if (target != null) currentTargets.add(target);
-                }
-                bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
-                bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
-                MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
+    public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) {
+        Bundle bundle = new Bundle();
+        ArrayList<AppTargetEvent> events = new ArrayList<>();
+        ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
+        workspaceItems.addAll(dataModel.appWidgets);
+        for (ItemInfo item : workspaceItems) {
+            AppTarget target = getAppTargetFromInfo(context, item);
+            if (target != null && !isTrackedForPrediction(item)) continue;
+            events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
+        }
+        ArrayList<AppTarget> currentTargets = new ArrayList<>();
+        FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+        if (hotseatItems != null) {
+            for (ItemInfo itemInfo : hotseatItems.items) {
+                AppTarget target = getAppTargetFromInfo(context, itemInfo);
+                if (target != null) currentTargets.add(target);
             }
-        });
+        }
+        bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
+        bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
+        return bundle;
     }
 
     /**
      * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
      * if item is not supported prediction
      */
-    public AppTarget getAppTargetFromInfo(ItemInfo info) {
+    public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
         if (info == null) return null;
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
                 && info instanceof LauncherAppWidgetInfo
@@ -107,17 +97,17 @@
                     shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
-                    mContext.getPackageName(), info.user).build();
+                    context.getPackageName(), info.user).build();
         }
         return null;
     }
 
-
     /**
      * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
      * location using {@link ItemInfo}
      */
-    public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+    public static AppTargetEvent wrapAppTargetWithLocation(
+            AppTarget target, int action, ItemInfo info) {
         String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
                 info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
                         ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 9e7c9fb..90f762e 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -19,8 +19,10 @@
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.content.Context;
+
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.GridBackupTable;
 import com.android.launcher3.provider.LauncherDbUtils;
@@ -29,29 +31,24 @@
  * A helper class to manage migration revert restoration for hybrid hotseat
  */
 public class HotseatRestoreHelper {
-    private final Launcher mLauncher;
-
-    HotseatRestoreHelper(Launcher context) {
-        mLauncher = context;
-    }
 
     /**
      * Creates a snapshot backup of Favorite table for future restoration use.
      */
-    public void createBackup() {
+    public static void createBackup(Context context) {
         MODEL_EXECUTOR.execute(() -> {
             try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
                     LauncherSettings.Settings.call(
-                            mLauncher.getContentResolver(),
+                            context.getContentResolver(),
                             LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
-                InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
-                GridBackupTable backupTable = new GridBackupTable(mLauncher,
+                InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+                GridBackupTable backupTable = new GridBackupTable(context,
                         transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
                 transaction.commit();
-                LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+                LauncherSettings.Settings.call(context.getContentResolver(),
                         LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE);
             }
         });
@@ -60,23 +57,23 @@
     /**
      * Finds and restores a previously saved snapshow of Favorites table
      */
-    public void restoreBackup() {
+    public static void restoreBackup(Context context) {
         MODEL_EXECUTOR.execute(() -> {
             try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
                     LauncherSettings.Settings.call(
-                            mLauncher.getContentResolver(),
+                            context.getContentResolver(),
                             LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
                 if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) {
                     return;
                 }
-                InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
-                GridBackupTable backupTable = new GridBackupTable(mLauncher,
+                InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+                GridBackupTable backupTable = new GridBackupTable(context,
                         transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
                 transaction.commit();
-                mLauncher.getModel().forceReload();
+                LauncherAppState.getInstance(context).getModel().forceReload();
             }
         });
     }
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 8e4c43f..364a321 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -15,8 +15,21 @@
  */
 package com.android.launcher3.model;
 
+import static android.app.prediction.AppTargetEvent.ACTION_DISMISS;
+import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;
+import static android.app.prediction.AppTargetEvent.ACTION_PIN;
+import static android.app.prediction.AppTargetEvent.ACTION_UNPIN;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -47,11 +60,12 @@
 import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
 import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
 import com.android.launcher3.logging.StatsLogManager.EventEnum;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
 
 import java.util.Locale;
-import java.util.function.Consumer;
+import java.util.function.ObjIntConsumer;
 import java.util.function.Predicate;
 
 /**
@@ -64,9 +78,11 @@
 
     private final Context mContext;
     private final Handler mMessageHandler;
-    private final Consumer<AppTargetEvent> mCallback;
+    private final ObjIntConsumer<AppTargetEvent> mCallback;
 
-    public AppEventProducer(Context context, Consumer<AppTargetEvent> callback) {
+    private LauncherAtom.ItemInfo mLastDragItem;
+
+    public AppEventProducer(Context context, ObjIntConsumer<AppTargetEvent> callback) {
         mContext = context;
         mMessageHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleMessage);
         mCallback = callback;
@@ -76,7 +92,7 @@
     private boolean handleMessage(Message msg) {
         switch (msg.what) {
             case MSG_LAUNCH: {
-                mCallback.accept((AppTargetEvent) msg.obj);
+                mCallback.accept((AppTargetEvent) msg.obj, msg.arg1);
                 return true;
             }
         }
@@ -84,13 +100,18 @@
     }
 
     @AnyThread
-    private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) {
-        AppTarget target = toAppTarget(atomInfo);
+    private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId, int targetPredictor) {
+        sendEvent(toAppTarget(atomInfo), atomInfo, eventId, targetPredictor);
+    }
+
+    @AnyThread
+    private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId,
+            int targetPredictor) {
         if (target != null) {
             AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
-                    .setLaunchLocation(getContainer(atomInfo))
+                    .setLaunchLocation(getContainer(locationInfo))
                     .build();
-            Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+            mMessageHandler.obtainMessage(MSG_LAUNCH, targetPredictor, 0, event).sendToTarget();
         }
     }
 
@@ -101,9 +122,42 @@
                 || event == LAUNCHER_TASK_LAUNCH_TAP
                 || event == LAUNCHER_QUICKSWITCH_RIGHT
                 || event == LAUNCHER_QUICKSWITCH_LEFT) {
-            sendEvent(atomInfo, AppTargetEvent.ACTION_LAUNCH);
+            sendEvent(atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
         } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
-            sendEvent(atomInfo, AppTargetEvent.ACTION_DISMISS);
+            sendEvent(atomInfo, ACTION_DISMISS, CONTAINER_PREDICTION);
+        } else if (event == LAUNCHER_ITEM_DRAG_STARTED) {
+            mLastDragItem = atomInfo;
+        } else if (event == LAUNCHER_ITEM_DROP_COMPLETED) {
+            if (mLastDragItem == null) {
+                return;
+            }
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+            if (isTrackedForHotseatPrediction(mLastDragItem)) {
+                sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+            mLastDragItem = null;
+        } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(createTempFolderTarget(), atomInfo, ACTION_PIN,
+                        CONTAINER_HOTSEAT_PREDICTION);
+                sendEvent(atomInfo, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_FOLDER_CONVERTED_TO_ICON) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(createTempFolderTarget(), atomInfo, ACTION_UNPIN,
+                        CONTAINER_HOTSEAT_PREDICTION);
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_ITEM_DROPPED_ON_REMOVE) {
+            if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
+                sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
         }
     }
 
@@ -152,10 +206,8 @@
                 }
                 break;
             }
-            case FOLDER_ICON: {
-                id = "folder:" + SystemClock.uptimeMillis();
-                cn = new ComponentName(mContext.getPackageName(), "#folder");
-            }
+            case FOLDER_ICON:
+                return createTempFolderTarget();
         }
         if (id != null && cn != null) {
             return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
@@ -165,6 +217,12 @@
         return null;
     }
 
+    private AppTarget createTempFolderTarget() {
+        return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()),
+                mContext.getPackageName(), Process.myUserHandle())
+                .build();
+    }
+
     private String getContainer(LauncherAtom.ItemInfo info) {
         ContainerInfo ci = info.getContainerInfo();
         switch (ci.getContainerCase()) {
@@ -212,11 +270,26 @@
     }
 
     private static String getHotseatContainerString(HotseatContainer hc) {
-        return String.format(Locale.ENGLISH, "hotseat/%d", hc.getIndex());
+        return String.format(Locale.ENGLISH, "hotseat/%1$d/[%1$d,0]/[1,1]", hc.getIndex());
     }
 
     private static ComponentName parseNullable(String componentNameString) {
         return TextUtils.isEmpty(componentNameString)
                 ? null : ComponentName.unflattenFromString(componentNameString);
     }
+
+    /**
+     * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
+     */
+    private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
+        ContainerInfo ci = info.getContainerInfo();
+        switch (ci.getContainerCase()) {
+            case HOTSEAT:
+                return true;
+            case WORKSPACE:
+                return ci.getWorkspace().getPageIndex() == 0;
+            default:
+                return false;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 166cb6c..be57dec 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -16,9 +16,11 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
 
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionManager;
@@ -62,6 +64,8 @@
 
     private final PredictorState mAllAppsState =
             new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
+    private final PredictorState mHotseatState =
+            new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions");
 
     private final InvariantDeviceProfile mIDP;
     private final AppEventProducer mAppEventProducer;
@@ -81,13 +85,23 @@
         // TODO: Implement caching and preloading
         super.loadItems(ums, pinnedShortcuts);
 
-        WorkspaceItemFactory factory =
+        WorkspaceItemFactory allAppsFactory =
                 new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns);
         mAllAppsState.items.setItems(
-                mAllAppsState.storage.read(mApp.getContext(), factory, ums.allUsers::get));
+                mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
         mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
 
+        WorkspaceItemFactory hotseatFactory =
+                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numHotseatIcons);
+        mHotseatState.items.setItems(
+                mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
+        mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
         mActive = true;
+    }
+
+    @Override
+    public void workspaceLoadComplete() {
+        super.workspaceLoadComplete();
         recreatePredictors();
     }
 
@@ -111,6 +125,7 @@
 
     private void destroyPredictors() {
         mAllAppsState.destroyPredictor();
+        mHotseatState.destroyPredictor();
     }
 
     @WorkerThread
@@ -125,18 +140,28 @@
             return;
         }
 
-        int count = mIDP.numAllAppsColumns;
-
-        mAllAppsState.predictor = apm.createAppPredictionSession(
+        registerPredictor(mAllAppsState, apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(context)
                         .setUiSurface("home")
-                        .setPredictedTargetCount(count)
-                        .build());
-        mAllAppsState.predictor.registerPredictionUpdates(
-                Executors.MODEL_EXECUTOR, t -> handleUpdate(mAllAppsState, t));
-        mAllAppsState.predictor.requestPredictionUpdate();
+                        .setPredictedTargetCount(mIDP.numAllAppsColumns)
+                        .build()));
+
+        // TODO: get bundle
+        registerPredictor(mHotseatState, apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(context)
+                        .setUiSurface("hotseat")
+                        .setPredictedTargetCount(mIDP.numHotseatIcons)
+                        .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
+                        .build()));
+
     }
 
+    private void registerPredictor(PredictorState state, AppPredictor predictor) {
+        state.predictor = predictor;
+        state.predictor.registerPredictionUpdates(
+                Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t));
+        state.predictor.requestPredictionUpdate();
+    }
 
     private void handleUpdate(PredictorState state, List<AppTarget> targets) {
         if (state.setTargets(targets)) {
@@ -154,9 +179,10 @@
         }
     }
 
-    private void onAppTargetEvent(AppTargetEvent event) {
-        if (mAllAppsState.predictor != null) {
-            mAllAppsState.predictor.notifyAppTargetEvent(event);
+    private void onAppTargetEvent(AppTargetEvent event, int client) {
+        PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
+        if (state.predictor != null) {
+            state.predictor.notifyAppTargetEvent(event);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f7bc0b3..e808f8c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -33,8 +33,8 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 
@@ -48,14 +48,11 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.Folder;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
-import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.testing.TestProtocol;
@@ -70,13 +67,14 @@
 import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
-import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.QuickstepOnboardingPrefs;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -84,7 +82,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.stream.Stream;
 
@@ -98,14 +95,7 @@
             SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
 
     private FixedContainerItems mAllAppsPredictions;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.createPredictor();
-        }
-    }
+    private HotseatPredictionController mHotseatPredictionController;
 
     @Override
     protected void setupViews() {
@@ -143,6 +133,18 @@
         }
     }
 
+    /**
+     * Returns Prediction controller for hybrid hotseat
+     */
+    public HotseatPredictionController getHotseatPredictionController() {
+        return mHotseatPredictionController;
+    }
+
+    @Override
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+        return new QuickstepOnboardingPrefs(this, sharedPrefs);
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -179,22 +181,6 @@
     }
 
     @Override
-    public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) {
-        super.folderCreatedFromItem(folder, itemInfo);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo());
-        }
-    }
-
-    @Override
-    public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {
-        super.folderConvertedToItem(folder, itemInfo);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo());
-        }
-    }
-
-    @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         if (mHotseatPredictionController != null) {
             return Stream.concat(super.getSupportedShortcuts(),
@@ -222,19 +208,14 @@
     }
 
     @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
-        super.bindPredictedItems(appInfos, ranks);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.showCachedItems(appInfos, ranks);
-        }
-    }
-
-    @Override
     public void bindExtraContainerItems(FixedContainerItems item) {
         if (item.containerId == Favorites.CONTAINER_PREDICTION) {
             mAllAppsPredictions = item;
             getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
                     .setPredictedApps(item.items);
+        } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION
+                && mHotseatPredictionController != null) {
+            mHotseatPredictionController.setPredictedItems(item);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 7eda627..b10adb4 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -26,13 +26,13 @@
 
 import android.content.SharedPreferences;
 
-import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.views.AllAppsEduView;
@@ -40,9 +40,9 @@
 /**
  * Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
  */
-public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
+public class QuickstepOnboardingPrefs extends OnboardingPrefs<QuickstepLauncher> {
 
-    public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
+    public QuickstepOnboardingPrefs(QuickstepLauncher launcher, SharedPreferences sharedPrefs) {
         super(launcher, sharedPrefs);
 
         StateManager<LauncherState> stateManager = launcher.getStateManager();
diff --git a/res/values/config.xml b/res/values/config.xml
index dc8bdff..fc0a5e1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,7 +69,6 @@
     <string name="app_launch_tracker_class" translatable="false"></string>
     <string name="test_information_handler_class" translatable="false"></string>
     <string name="launcher_activity_logic_class" translatable="false"></string>
-    <string name="prediction_model_class" translatable="false"></string>
     <string name="model_delegate_class" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index b27abc4..fd4c30c 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -113,18 +113,6 @@
 
             return res;
         }
-
-
-        /**
-         * This is used to determine if an object is dropped at a different location than it was
-         * dragged from
-         */
-        public boolean isMoved() {
-            return dragInfo.cellX != originalDragInfo.cellX
-                    || dragInfo.cellY != originalDragInfo.cellY
-                    || dragInfo.screenId != originalDragInfo.screenId
-                    || dragInfo.container != originalDragInfo.container;
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 53b65e8..cbb577a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -108,7 +108,6 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.IconCache;
@@ -1743,16 +1742,6 @@
     }
 
     /**
-     * Called when a workspace item is converted into a folder
-     */
-    public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){}
-
-    /**
-     * Called when a folder is converted into a workspace item
-     */
-    public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {}
-
-    /**
      * Unbinds the view for the specified item, and removes the item and all its children.
      *
      * @param v the view being removed.
@@ -2189,9 +2178,6 @@
         workspace.requestLayout();
     }
 
-    @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
     /**
      * Add the views for a widget to the workspace.
      */
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 782a869..b278e81 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -33,7 +33,6 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.PredictionModel;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
@@ -58,7 +57,6 @@
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
-    private final PredictionModel mPredictionModel;
 
     private SecureSettingsObserver mNotificationDotsObserver;
     private InstallSessionTracker mInstallSessionTracker;
@@ -125,7 +123,6 @@
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(context, this, mIconCache, AppFilter.newInstance(mContext));
-        mPredictionModel = PredictionModel.newInstance(mContext);
     }
 
     protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -182,10 +179,6 @@
         return mModel;
     }
 
-    public PredictionModel getPredictionModel() {
-        return mPredictionModel;
-    }
-
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f58e0b4..6aa4c43 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -218,6 +218,13 @@
     }
 
     /**
+     * Called when the workspace items have drastically changed
+     */
+    public void onWorkspaceUiChanged() {
+        MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete);
+    }
+
+    /**
      * Called when the model is destroyed
      */
     public void destroy() {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3be9ac7..252e64e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1702,7 +1702,6 @@
                 fi.addItem(destInfo);
                 fi.addItem(sourceInfo);
             }
-            mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
             return true;
         }
         return false;
@@ -1719,7 +1718,7 @@
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
             if (fi.acceptDrop(d.dragInfo)) {
-                mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
+                mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                         .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
                 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
 
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b91d1c3..61543c4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
 
@@ -1183,7 +1184,8 @@
                         newIcon.requestFocus();
                     }
                     if (finalItem != null) {
-                        mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem);
+                        mStatsLogManager.logger().withItemInfo(finalItem)
+                                .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
                     }
                 }
             }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index cca9836..0f1432a 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -19,6 +19,7 @@
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 import static android.view.View.VISIBLE;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
@@ -72,12 +73,12 @@
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.LoaderResults;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -95,6 +96,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -467,12 +469,14 @@
             }
             IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
                     mIdp.numHotseatIcons);
-            int count = Math.min(ranks.size(), workspaceResult.mCachedPredictedItems.size());
+            List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
+                    ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
+            int count = Math.min(ranks.size(), predictions.size());
             for (int i = 0; i < count; i++) {
-                AppInfo appInfo = workspaceResult.mCachedPredictedItems.get(i);
                 int rank = ranks.get(i);
-                WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(appInfo);
-                itemInfo.container = Favorites.CONTAINER_HOTSEAT_PREDICTION;
+                WorkspaceItemInfo itemInfo =
+                        new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
+                itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
                 itemInfo.rank = rank;
                 itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
                 itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
@@ -569,8 +573,7 @@
                 return null;
             }
 
-            return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
-                    mBgDataModel.cachedPredictedItems, mBgDataModel.widgetsModel, null);
+            return new WorkspaceResult(mBgDataModel, mBgDataModel.widgetsModel, null);
         }
     }
 
@@ -594,11 +597,10 @@
         }
 
         @Override
-        public WorkspaceResult call() throws Exception {
+        public WorkspaceResult call() {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
             loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI);
-            return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
-                    mBgDataModel.cachedPredictedItems, null, mWidgetProvidersMap);
+            return new WorkspaceResult(mBgDataModel, null, mWidgetProvidersMap);
         }
     }
 
@@ -618,17 +620,16 @@
     private static class WorkspaceResult {
         private final ArrayList<ItemInfo> mWorkspaceItems;
         private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
-        private final ArrayList<AppInfo> mCachedPredictedItems;
+        private final FixedContainerItems mHotseatPredictions;
         private final WidgetsModel mWidgetsModel;
         private final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
 
-        private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
-                ArrayList<LauncherAppWidgetInfo> appWidgets,
-                ArrayList<AppInfo> cachedPredictedItems, WidgetsModel widgetsModel,
+        private WorkspaceResult(BgDataModel dataModel,
+                WidgetsModel widgetsModel,
                 Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
-            mWorkspaceItems = workspaceItems;
-            mAppWidgets = appWidgets;
-            mCachedPredictedItems = cachedPredictedItems;
+            mWorkspaceItems = dataModel.workspaceItems;
+            mAppWidgets = dataModel.appWidgets;
+            mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
             mWidgetsModel = widgetsModel;
             mWidgetProvidersMap = widgetProviderInfoMap;
         }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2282339..ec1c3ef 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -316,7 +316,13 @@
         LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625),
 
         @UiEvent(doc = "User tapped on image content in Overview Select mode.")
-        LAUNCHER_SELECT_MODE_IMAGE(627);
+        LAUNCHER_SELECT_MODE_IMAGE(627),
+
+        @UiEvent(doc = "A folder was replaced by a single item")
+        LAUNCHER_FOLDER_CONVERTED_TO_ICON(628),
+
+        @UiEvent(doc = "A hotseat prediction item was pinned")
+        LAUNCHER_HOTSEAT_PREDICTION_PINNED(629);
 
         // ADD MORE
 
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 586333f..5c85bab 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
 
 import android.util.Log;
@@ -206,9 +205,6 @@
             mExtraItems.forEach(item ->
                     executeCallbacksTask(c -> c.bindExtraContainerItems(item), mainExecutor));
 
-            // Locate available spots for prediction using currentWorkspaceItems
-            IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
-            bindPredictedItems(gaps, mainExecutor);
             // In case of validFirstPage, only bind the first screen, and defer binding the
             // remaining screens after first onDraw (and an optional the fade animation whichever
             // happens later).
@@ -261,11 +257,6 @@
             }
         }
 
-        private void bindPredictedItems(IntArray ranks, final Executor executor) {
-            ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
-            executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
-        }
-
         protected void executeCallbacksTask(CallbackTask task, Executor executor) {
             executor.execute(() -> {
                 if (mMyBindingId != mBgDataModel.lastBindId) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 140342f..1fc897a 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -104,11 +104,6 @@
     public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
 
     /**
-     * List of all cached predicted items visible on home screen
-     */
-    public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
-
-    /**
      * Maps all launcher activities to counts of their shortcuts.
      */
     public final HashMap<ComponentKey, Integer> deepShortcutMap = new HashMap<>();
@@ -472,10 +467,5 @@
         default void bindExtraContainerItems(FixedContainerItems item) { }
 
         void bindAllApplications(AppInfo[] apps, int flags);
-
-        /**
-         * Binds predicted appInfos at at available prediction slots.
-         */
-        void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks);
     }
 }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 1dd8c11..983ec0c 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -48,8 +48,6 @@
 import android.util.LongSparseArray;
 import android.util.TimingLogger;
 
-import androidx.annotation.WorkerThread;
-
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
@@ -190,13 +188,13 @@
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
             loadWorkspace(allShortcuts);
-            loadCachedPredictions();
             logger.addSplit("loadWorkspace");
 
             verifyNotStopped();
             mResults.bindWorkspace();
             logger.addSplit("bindWorkspace");
 
+            mModelDelegate.workspaceLoadComplete();
             // Notify the installer packages of packages with active installs on the first screen.
             sendFirstScreenActiveInstallsBroadcast();
             logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
@@ -839,24 +837,6 @@
         }
     }
 
-    @WorkerThread
-    private void loadCachedPredictions() {
-        synchronized (mBgDataModel) {
-            List<ComponentKey> componentKeys =
-                    mApp.getPredictionModel().getPredictionComponentKeys();
-            List<LauncherActivityInfo> l;
-            mBgDataModel.cachedPredictedItems.clear();
-            for (ComponentKey key : componentKeys) {
-                l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
-                if (l.size() == 0) continue;
-                AppInfo info = new AppInfo(l.get(0), key.user,
-                        mUserManagerState.isUserQuiet(key.user));
-                mBgDataModel.cachedPredictedItems.add(info);
-                mIconCache.getTitleAndIcon(info, false);
-            }
-        }
-    }
-
     private void sanitizeData() {
         Context context = mApp.getContext();
         if (mItemsDeleted) {
@@ -900,14 +880,6 @@
                         PackageInstallInfo.fromInstallingState(info));
             }
         }
-        for (AppInfo item : mBgDataModel.cachedPredictedItems) {
-            List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
-                    item.componentName.getPackageName(), item.user);
-            for (LauncherActivityInfo info : l) {
-                boolean quietMode = mUserManagerState.isUserQuiet(item.user);
-                mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
-            }
-        }
 
         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
                 mUserManagerState.isAnyProfileQuietModeEnabled());
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 53e8a86..3ed8809 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -72,6 +72,12 @@
     public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
 
     /**
+     * Called during loader after workspace loading is complete
+     */
+    @WorkerThread
+    public void workspaceLoadComplete() { }
+
+    /**
      * Called when the delegate is no loner needed
      */
     @WorkerThread
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
deleted file mode 100644
index cb3903d..0000000
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.UserHandle;
-
-import androidx.annotation.AnyThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Model Helper for app predictions
- */
-public class PredictionModel implements ResourceBasedOverride {
-
-    private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
-    private static final int MAX_CACHE_ITEMS = 5;
-
-    protected Context mContext;
-    private SharedPreferences mDevicePrefs;
-    private UserCache mUserCache;
-
-
-    /**
-     * Retrieve instance of this object that can be overridden in runtime based on the build
-     * variant of the application.
-     */
-    public static PredictionModel newInstance(Context context) {
-        PredictionModel model = Overrides.getObject(PredictionModel.class, context,
-                R.string.prediction_model_class);
-        model.init(context);
-        return model;
-    }
-
-    protected void init(Context context) {
-        mContext = context;
-        mDevicePrefs = Utilities.getDevicePrefs(mContext);
-        mUserCache = UserCache.INSTANCE.get(mContext);
-
-    }
-
-    /**
-     * Formats and stores a list of component key in device preferences.
-     */
-    @AnyThread
-    public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
-        MODEL_EXECUTOR.execute(() -> {
-            LauncherAppState appState = LauncherAppState.getInstance(mContext);
-            StringBuilder builder = new StringBuilder();
-            int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
-            for (int i = 0; i < count; i++) {
-                builder.append(serializeComponentKeyToString(componentKeys.get(i)));
-                builder.append("\n");
-            }
-            if (componentKeys.isEmpty() /* should invalidate loader items */) {
-                appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
-                    @Override
-                    public void execute(LauncherAppState app, BgDataModel model, AllAppsList apps) {
-                        model.cachedPredictedItems.clear();
-                    }
-                });
-            }
-            mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
-        });
-    }
-
-    /**
-     * parses and returns ComponentKeys saved by
-     * {@link PredictionModel#cachePredictionComponentKeys(List)}
-     */
-    @WorkerThread
-    public List<ComponentKey> getPredictionComponentKeys() {
-        Preconditions.assertWorkerThread();
-        ArrayList<ComponentKey> items = new ArrayList<>();
-        String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
-        for (String line : cachedBlob.split("\n")) {
-            ComponentKey key = getComponentKeyFromSerializedString(line);
-            if (key != null) {
-                items.add(key);
-            }
-
-        }
-        return items;
-    }
-
-    private String serializeComponentKeyToString(ComponentKey componentKey) {
-        long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
-        return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
-    }
-
-    private ComponentKey getComponentKeyFromSerializedString(String str) {
-        int sep = str.indexOf('#');
-        if (sep < 0 || (sep + 1) >= str.length()) {
-            return null;
-        }
-        ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
-        if (componentName == null) {
-            return null;
-        }
-        try {
-            long serialNumber = Long.parseLong(str.substring(sep + 1));
-            UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
-            return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
-        } catch (NumberFormatException ex) {
-            return null;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 31c3014..2b04365 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -197,9 +197,6 @@
     public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
 
     @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
-    @Override
     public void bindScreens(IntArray orderedScreenIds) { }
 
     @Override
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 6e5e7d9..1b33197 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -44,7 +44,8 @@
      */
     @StringDef(value = {
             HOME_BOUNCE_SEEN,
-            SHELF_BOUNCE_SEEN
+            SHELF_BOUNCE_SEEN,
+            HOTSEAT_LONGPRESS_TIP_SEEN
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventBoolKey {}