Merge "Disabling flaky test until fix is found" into rvc-dev
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 0fe5310..9423cb2 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -63,14 +63,14 @@
 
   // Note: proto does not support duplicate enum values, even if they belong to different enum type.
   // Hence "FROM" and "TO" prefix added.
-  enum FromFolderLabelState{
+  enum FromFolderLabelState {
     FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
     FROM_EMPTY = 1;
     FROM_CUSTOM = 2;
     FROM_SUGGESTED = 3;
   }
 
-  enum ToFolderLabelState{
+  enum ToFolderLabelState {
     TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
     TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
     TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
@@ -79,10 +79,14 @@
     TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
     TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
     TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
-    TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
+    TO_EMPTY_WITH_VALID_SUGGESTIONS = 8 [deprecated = true];
+    TO_EMPTY_WITH_VALID_PRIMARY = 15;
+    TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 16;
     TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
     TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
-    TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
+    TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11 [deprecated = true];
+    TO_CUSTOM_WITH_VALID_PRIMARY = 17;
+    TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 18;
     TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
     TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
     UNCHANGED = 14;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
index 0712285..d200868 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -18,10 +18,9 @@
 
 import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.util.ComponentKey;
 
 public class ComponentKeyMapper {
@@ -57,9 +56,8 @@
             return item;
         } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
             return mCache.getInstantApp(componentKey.componentName.getPackageName());
-        } else if (componentKey instanceof ShortcutKey) {
-            return mCache.getShortcutInfo((ShortcutKey) componentKey);
+        } else {
+            return mCache.getShortcutInfo(componentKey);
         }
-        return null;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 54f58e2..ab96b1340 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -38,13 +38,14 @@
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.WorkspaceItemInfo;
 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;
@@ -76,7 +77,7 @@
     private final Runnable mOnUpdateCallback;
     private final IconCache mIconCache;
 
-    private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
+    private final Map<ComponentKey, WorkspaceItemInfo> mShortcuts;
     private final Map<String, InstantAppItemInfo> mInstantApps;
 
     public DynamicItemCache(Context context, Runnable onUpdateCallback) {
@@ -230,7 +231,7 @@
     }
 
     @MainThread
-    public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
+    public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
         return mShortcuts.get(key);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
index 6e5f461..6c4c601 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
@@ -21,9 +21,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 public class InstantAppItemInfo extends AppInfo {
 
@@ -44,7 +44,7 @@
         workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON
                 | WorkspaceItemInfo.FLAG_RESTORE_STARTED
                 | WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
-        workspaceItemInfo.intent.setPackage(componentName.getPackageName());
+        workspaceItemInfo.getIntent().setPackage(componentName.getPackageName());
         return workspaceItemInfo;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 8faec46..d4cc129 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -38,18 +38,14 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
@@ -60,6 +56,10 @@
 import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.data.AppInfo;
+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.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 632b9b5..8e55609 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -28,8 +28,6 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
@@ -40,6 +38,8 @@
 import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 773c6c8..78cc2dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -27,16 +27,16 @@
 import androidx.core.app.NotificationCompat;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.GridOccupancy;
@@ -200,6 +200,7 @@
             pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
                     LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
                     .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+            mNewScreens = IntArray.wrap(pageId);
         }
         for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
             View child = mHotseat.getChildAt(i, 0);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index cb46c40..66c60bc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -16,8 +16,7 @@
 package com.android.launcher3.hybridhotseat;
 
 import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
-        .HYBRID_HOTSEAT_CANCELED;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
 
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
@@ -34,10 +33,11 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.views.AbstractSlideInView;
@@ -153,6 +153,12 @@
     private void logUserAction(boolean migrated, int pageIndex) {
         LauncherLogProto.Action action = new LauncherLogProto.Action();
         LauncherLogProto.Target target = new LauncherLogProto.Target();
+
+        int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount();
+        // -1 to exclude smart space
+        int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
+                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
+
         action.type = LauncherLogProto.Action.Type.TOUCH;
         action.touch = LauncherLogProto.Action.Touch.TAP;
         target.containerType = LauncherLogProto.ContainerType.TIP;
@@ -162,7 +168,7 @@
         target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
         // encoding migration type on pageIndex
         target.pageIndex = pageIndex;
-        target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION;
+        target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
         LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
         UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 9bc0975..1aff8e9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -31,6 +31,7 @@
 import android.app.prediction.AppTargetId;
 import android.content.ComponentName;
 import android.os.Bundle;
+import android.os.Process;
 import android.provider.DeviceConfig;
 import android.util.Log;
 import android.view.View;
@@ -39,22 +40,18 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.appprediction.ComponentKeyMapper;
@@ -64,6 +61,12 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.UserEventDispatcher;
+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.ItemInfoWithIcon;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+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;
@@ -76,6 +79,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.OptionalInt;
 import java.util.stream.IntStream;
 
@@ -91,18 +95,19 @@
     private static final String TAG = "PredictiveHotseat";
     private static final boolean DEBUG = false;
 
-    public static final int MAX_ITEMS_FOR_MIGRATION = DeviceConfig.getInt(
-            DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
-
     //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
     private static final int APPTARGET_ACTION_UNPIN = 4;
 
+    private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys";
+
     private static final String APP_LOCATION_HOTSEAT = "hotseat";
     private static final String APP_LOCATION_WORKSPACE = "workspace";
 
     private static final String BUNDLE_KEY_HOTSEAT = "hotseat_apps";
     private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
 
+    private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
+
     private static final String PREDICTION_CLIENT = "hotseat";
     private DropTarget.DragObject mDragObject;
     private int mHotSeatItemsCount;
@@ -119,6 +124,7 @@
     private AllAppsStore mAllAppsStore;
     private AnimatorSet mIconRemoveAnimators;
     private boolean mUIUpdatePaused = false;
+    private boolean mRequiresCacheUpdate = false;
 
     private HotseatEduController mHotseatEduController;
 
@@ -145,6 +151,7 @@
         if (mHotseat.isAttachedToWindow()) {
             onViewAttachedToWindow(mHotseat);
         }
+        showCachedItems();
     }
 
     /**
@@ -294,17 +301,57 @@
         mAppPredictor.requestPredictionUpdate();
     }
 
+    private void showCachedItems() {
+        ArrayList<ComponentKey> componentKeys = getCachedComponentKeys();
+        mComponentKeyMappers.clear();
+        for (ComponentKey key : componentKeys) {
+            mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
+        }
+        updateDependencies();
+        fillGapsWithPrediction();
+    }
+
     private Bundle getAppPredictionContextExtra() {
         Bundle bundle = new Bundle();
+
+        //TODO: remove this way of reporting items
         bundle.putParcelableArrayList(BUNDLE_KEY_HOTSEAT,
                 getPinnedAppTargetsInViewGroup((mHotseat.getShortcutsAndWidgets())));
         bundle.putParcelableArrayList(BUNDLE_KEY_WORKSPACE, getPinnedAppTargetsInViewGroup(
                 mLauncher.getWorkspace().getScreenWithId(
                         Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets()));
 
+        ArrayList<AppTargetEvent> pinEvents = new ArrayList<>();
+        getPinEventsForViewGroup(pinEvents, mHotseat.getShortcutsAndWidgets(),
+                APP_LOCATION_HOTSEAT);
+        getPinEventsForViewGroup(pinEvents, mLauncher.getWorkspace().getScreenWithId(
+                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets(), APP_LOCATION_WORKSPACE);
+        bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, pinEvents);
+
         return bundle;
     }
 
+    private ArrayList<AppTargetEvent> getPinEventsForViewGroup(ArrayList<AppTargetEvent> pinEvents,
+            ViewGroup views, String root) {
+        for (int i = 0; i < views.getChildCount(); i++) {
+            View child = views.getChildAt(i);
+            final AppTargetEvent event;
+            if (child.getTag() instanceof ItemInfo && getAppTargetFromInfo(
+                    (ItemInfo) child.getTag()) != null) {
+                ItemInfo info = (ItemInfo) child.getTag();
+                event = wrapAppTargetWithLocation(getAppTargetFromInfo(info),
+                        AppTargetEvent.ACTION_PIN, info);
+            } else {
+                CellLayout.LayoutParams params = (CellLayout.LayoutParams) views.getLayoutParams();
+                event = wrapAppTargetWithLocation(getBlockAppTarget(), AppTargetEvent.ACTION_PIN,
+                        root, 0, params.cellX, params.cellY, params.cellHSpan, params.cellVSpan);
+            }
+            pinEvents.add(event);
+        }
+        return pinEvents;
+    }
+
+
     private ArrayList<AppTarget> getPinnedAppTargetsInViewGroup(ViewGroup viewGroup) {
         ArrayList<AppTarget> pinnedApps = new ArrayList<>();
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
@@ -320,6 +367,7 @@
     private void setPredictedApps(List<AppTarget> appTargets) {
         mComponentKeyMappers.clear();
         StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
+        ArrayList<ComponentKey> componentKeys = new ArrayList<>();
         for (AppTarget appTarget : appTargets) {
             ComponentKey key;
             if (appTarget.getShortcutInfo() != null) {
@@ -328,6 +376,7 @@
                 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());
@@ -342,6 +391,35 @@
         } else if (mHotseatEduController != null) {
             mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
         }
+        // should invalidate cache if AiAi sends empty list of AppTargets
+        if (appTargets.isEmpty()) {
+            mRequiresCacheUpdate = true;
+        }
+        cachePredictionComponentKeys(componentKeys);
+    }
+
+    private void cachePredictionComponentKeys(ArrayList<ComponentKey> componentKeys) {
+        if (!mRequiresCacheUpdate) return;
+        StringBuilder builder = new StringBuilder();
+        for (ComponentKey componentKey : componentKeys) {
+            builder.append(componentKey);
+            builder.append("\n");
+        }
+        mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY,
+                builder.toString()).apply();
+        mRequiresCacheUpdate = false;
+    }
+
+    private ArrayList<ComponentKey> getCachedComponentKeys() {
+        String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, "");
+        ArrayList<ComponentKey> results = new ArrayList<>();
+        for (String line : cachedBlob.split("\n")) {
+            ComponentKey key = ComponentKey.fromString(line);
+            if (key != null) {
+                results.add(key);
+            }
+        }
+        return results;
     }
 
     private void updateDependencies() {
@@ -367,6 +445,7 @@
         icon.pin(workspaceItemInfo);
         AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
         notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
+        mRequiresCacheUpdate = true;
     }
 
     private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
@@ -533,6 +612,7 @@
         }
         mDragObject = null;
         fillGapsWithPrediction(true, this::removeOutlineDrawings);
+        mRequiresCacheUpdate = true;
     }
 
     @Nullable
@@ -606,6 +686,9 @@
         if (isReady()) return;
         int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount();
 
+        int maxItems = DeviceConfig.getInt(
+                DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
+
         // -1 to exclude smart space
         int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
                 Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
@@ -614,7 +697,7 @@
         // open spots in their hotseat and have more than maxItems in their hotseat + workspace
 
         if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount
-                > MAX_ITEMS_FOR_MIGRATION) {
+                > maxItems) {
             mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN,
                     true).apply();
 
@@ -626,8 +709,8 @@
 
             // temporarily encode details in log target (go/hotseat_migration)
             target.rank = 2;
-            target.cardinality = MAX_ITEMS_FOR_MIGRATION;
-            target.pageIndex = (workspaceItemCount * 1000) + hotseatItemsCount;
+            target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
+            target.pageIndex = maxItems;
             LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
             UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null);
 
@@ -689,4 +772,52 @@
         return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
                 cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
     }
+
+    private AppTarget getAppTargetFromInfo(ItemInfo info) {
+        if (info == null) return null;
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                && info instanceof LauncherAppWidgetInfo
+                && ((LauncherAppWidgetInfo) info).providerName != null) {
+            ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
+            return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
+                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && info.getTargetComponent() != null) {
+            ComponentName cn = info.getTargetComponent();
+            return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
+                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && info instanceof WorkspaceItemInfo) {
+            ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
+            //TODO: switch to using full shortcut info
+            return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
+                    shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+            return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
+                    mLauncher.getPackageName(), info.user).build();
+        }
+        return null;
+    }
+
+    private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+        return wrapAppTargetWithLocation(target, action,
+                info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                        ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, info.screenId, info.cellX,
+                info.cellY, info.spanX, info.spanY);
+    }
+
+    private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, String root,
+            int screenId, int x, int y, int spanX, int spanY) {
+        return new AppTargetEvent.Builder(target, action).setLaunchLocation(
+                String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", root, screenId, x, y, spanX,
+                        spanY)).build();
+    }
+
+    /**
+     * A helper method to generate an AppTarget that's used to communicate workspace layout
+     */
+    private AppTarget getBlockAppTarget() {
+        return new AppTarget.Builder(new AppTargetId("block"),
+                mLauncher.getPackageName(), Process.myUserHandle()).build();
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 304c77f..8af26c6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -33,14 +33,14 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
@@ -136,9 +136,11 @@
 
     @Override
     public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) {
-        accessibilityNodeInfo.addAction(
-                new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
-                        getContext().getText(R.string.pin_prediction)));
+        if (!mIsPinned) {
+            accessibilityNodeInfo.addAction(
+                    new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
+                            getContext().getText(R.string.pin_prediction)));
+        }
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index a02d9c3..1110df7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -28,14 +28,14 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index f81c56f..fadde37 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -46,11 +46,8 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.model.PagedViewOrientedState;
-import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
@@ -59,6 +56,7 @@
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -127,7 +125,7 @@
     protected boolean mCanceled;
     protected int mFinishingRecentsAnimationForNewTaskId = -1;
 
-    private PagedViewOrientedState mOrientedState;
+    private RecentsOrientedState mOrientedState;
 
     protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             GestureState gestureState, InputConsumerController inputConsumer) {
@@ -199,18 +197,12 @@
     }
 
     protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask1");
-        }
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // We finish recents animation inside launchTask() when live tile is enabled.
             mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
                     true /* freezeTaskList */);
         } else {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask2");
-            }
             int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
             if (!mCanceled) {
                 TaskView nextTask = mRecentsView.getTaskView(taskId);
@@ -337,9 +329,11 @@
             // TODO(b/150300347): The first recents animation after launcher is started with the
             //  foreground app not in landscape will look funky until that bug is fixed
             displayRotation = mOrientedState.getDisplayRotation();
+
+            RectF tempRectF = new RectF(TEMP_RECT);
+            mOrientedState.mapRectFromNormalOrientation(tempRectF, dp.widthPx, dp.heightPx);
+            tempRectF.roundOut(TEMP_RECT);
         }
-        RotationHelper.getTargetRectForRotation(TEMP_RECT, dp.widthPx, dp.heightPx,
-            displayRotation);
         mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
         if (mDeviceState.isFullyGesturalNavMode()) {
             // We can drag all the way to the top of the screen.
@@ -434,7 +428,7 @@
 
     protected PagedOrientationHandler getOrientationHandler() {
         if (mOrientedState == null) {
-            return new PortraitPagedViewHandler();
+            return PagedOrientationHandler.PORTRAIT;
         }
         return mOrientedState.getOrientationHandler();
     }
@@ -455,8 +449,8 @@
                     .setTargetSet(mRecentsAnimationTargets)
                     .setLauncherOnTop(false)));
         if (isFloatingIconView) {
-            RotationHelper.mapInverseRectFromNormalOrientation(startRect,
-                mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
+            mOrientedState.mapInverseRectFromNormalOrientation(
+                    startRect, mDp.widthPx, mDp.heightPx);
         }
         RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
         if (isFloatingIconView) {
@@ -487,8 +481,8 @@
 
                 rotatedRect.set(currentRect);
                 if (isFloatingIconView) {
-                    RotationHelper.mapRectFromNormalOrientation(rotatedRect,
-                        mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
+                    mOrientedState.mapRectFromNormalOrientation(
+                            rotatedRect, mDp.widthPx, mDp.heightPx);
                     mTransformParams.setCornerRadius(endRadius * progress + startRadius
                         * (1f - progress));
                 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 4abb86a..2214dd0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -33,8 +33,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.ActivityInitListener;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index da73bc0..46b026e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -40,9 +40,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.touch.LandscapePagedViewHandler;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
@@ -503,8 +501,8 @@
                 PagedOrientationHandler orientationHandler = mRecentsView != null
                         ? mRecentsView.getPagedOrientationHandler()
                         : (mDp.isLandscape
-                                ? new LandscapePagedViewHandler()
-                                : new PortraitPagedViewHandler());
+                                ? PagedOrientationHandler.LANDSCAPE
+                                : PagedOrientationHandler.PORTRAIT);
                 return HomeAnimationFactory
                     .getDefaultWindowTargetRect(orientationHandler, mDp);
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 4cfa6f1..31a2814 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -984,9 +984,6 @@
             windowAnim.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
-                    if (TestProtocol.sDebugTracing) {
-                        Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationSuccess");
-                    }
                     if (mRecentsAnimationController == null) {
                         // If the recents animation is interrupted, we still end the running
                         // animation (not canceled) so this is still called. In that case, we can
@@ -1210,9 +1207,6 @@
     }
 
     private void switchToScreenshot() {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_START_FROM_RECENTS, "switchToScreenshot");
-        }
         final int runningTaskId = mGestureState.getRunningTaskId();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationController != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index 9ba2e5a..021d39d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -35,8 +35,8 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -71,7 +71,7 @@
         WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
         dummyInfo.intent = new Intent();
         ComponentName component = task.getTopComponent();
-        dummyInfo.intent.setComponent(component);
+        dummyInfo.getIntent().setComponent(component);
         dummyInfo.user = UserHandle.of(task.key.userId);
         dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
         return dummyInfo;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 6a3e1fe..47c07af 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -33,8 +33,8 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index c889632..2c2feb2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -26,11 +26,12 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -38,6 +39,7 @@
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.graphics.Region;
+import android.graphics.drawable.Icon;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -46,6 +48,7 @@
 import android.view.Choreographer;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.Nullable;
@@ -53,8 +56,8 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.AppLaunchTracker;
@@ -64,6 +67,7 @@
 import com.android.launcher3.tracing.nano.LauncherTraceProto;
 import com.android.launcher3.tracing.nano.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -126,6 +130,12 @@
     private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
     private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
 
+    /**
+     * System Action ID to show all apps.  This ID should follow the ones in
+     * com.android.systemui.accessibility.SystemActions.
+     */
+    private static final int SYSTEM_ACTION_ID_ALL_APPS = 100;
+
     private int mBackGestureNotificationCounter = -1;
     @Nullable
     private OverscrollPlugin mOverscrollPlugin;
@@ -351,6 +361,9 @@
 
         PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
                 OverscrollPlugin.class, false /* allowMultiple */);
+
+        mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
+        onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
 
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
@@ -365,11 +378,29 @@
         if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
             sharedPrefs.edit()
                     .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
-                    .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
+                    .putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false)
                     .apply();
         }
     }
 
+    private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
+        AccessibilityManager am = getSystemService(AccessibilityManager.class);
+
+        if (isHomeAndOverviewSame) {
+            Intent intent = new Intent(mOverviewComponentObserver.getHomeIntent())
+                    .setAction(Intent.ACTION_ALL_APPS);
+            RemoteAction allAppsAction = new RemoteAction(
+                    Icon.createWithResource(this, R.drawable.ic_apps),
+                    getString(R.string.all_apps_label),
+                    getString(R.string.all_apps_label),
+                    PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
+                            PendingIntent.FLAG_UPDATE_CURRENT));
+            am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
+        } else {
+            am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+        }
+    }
+
     @UiThread
     private void onSystemUiFlagsChanged() {
         if (mDeviceState.isUserUnlocked()) {
@@ -408,6 +439,9 @@
         ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
         ProtoTracer.INSTANCE.get(this).remove(this);
 
+        getSystemService(AccessibilityManager.class)
+                .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
+
         sConnected = false;
         super.onDestroy();
     }
@@ -451,7 +485,7 @@
                 mConsumer.onConsumerAboutToBeSwitched();
                 mConsumer = newConsumer(prevGestureState, newGestureState, event);
 
-                ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
+                ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
                 mUncheckedConsumer = mConsumer;
             } else if (mDeviceState.isUserUnlocked()
                     && mDeviceState.isFullyGesturalNavMode()
@@ -481,14 +515,14 @@
         }
 
         ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
+        boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
+                && mConsumer != null
+                && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
         mUncheckedConsumer.onMotionEvent(event);
 
-        if (action == ACTION_UP || action == ACTION_CANCEL) {
-            if (mConsumer != null && !mConsumer.isConsumerDetachedFromGesture()) {
-                onConsumerInactive(mConsumer);
-            }
+        if (cleanUpConsumer) {
+            reset();
         }
-
         TraceHelper.INSTANCE.endFlagsOverride(traceToken);
     }
 
@@ -496,7 +530,7 @@
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
         gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
-                () -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
+                () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
         return gestureState;
     }
 
@@ -505,6 +539,8 @@
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
         if (!mDeviceState.isUserUnlocked()) {
+            Log.d(TAG, "User locked. Can start system gesture? " + canStartSystemGesture
+                + " sysUiFlags: " + mDeviceState.getSystemUiStateFlags());
             if (canStartSystemGesture) {
                 // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
@@ -596,10 +632,18 @@
             return createDeviceLockedInputConsumer(gestureState);
         }
 
-        RunningTaskInfo runningTask = gestureState.getRunningTask();
-        ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
-        boolean forceOverviewInputConsumer = runningTask != null
-                && runningTask.baseIntent.getComponent().equals(homeComponent);
+        boolean forceOverviewInputConsumer = false;
+        if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
+            // In the case where we are in the excluded assistant state, ignore it and treat the
+            // running activity as the task behind the assistant
+            gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant",
+                    () -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
+            ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
+            ComponentName runningComponent =
+                    gestureState.getRunningTask().baseIntent.getComponent();
+            forceOverviewInputConsumer =
+                    runningComponent != null && runningComponent.equals(homeComponent);
+        }
 
         if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
             // If the finish animation was interrupted, then continue using the other activity input
@@ -678,15 +722,21 @@
     }
 
     /**
-     * To be called by the consumer when it's no longer active.
+     * To be called by the consumer when it's no longer active. This can be called by any consumer
+     * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts
+     * intercepting touches, the base consumer can try to call this).
      */
     private void onConsumerInactive(InputConsumer caller) {
-        if (mConsumer != null && mConsumer.isInConsumerHierarchy(caller)) {
-            mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
-            mGestureState = new GestureState();
+        if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
+            reset();
         }
     }
 
+    private void reset() {
+        mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
+        mGestureState = new GestureState();
+    }
+
     private void preloadOverview(boolean fromInit) {
         if (!mDeviceState.isUserUnlocked()) {
             return;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index bcc9707..67a15a7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -25,13 +25,11 @@
     }
 
     @Override
-    public boolean isConsumerDetachedFromGesture() {
-        return mDelegate.isConsumerDetachedFromGesture();
-    }
-
-    @Override
-    public boolean isInConsumerHierarchy(InputConsumer candidate) {
-        return this == candidate || mDelegate.isInConsumerHierarchy(candidate);
+    public InputConsumer getActiveConsumerInHierarchy() {
+        if (mState == STATE_ACTIVE) {
+            return this;
+        }
+        return mDelegate.getActiveConsumerInHierarchy();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
index b9ef57e..e000803 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
@@ -19,8 +19,8 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 import java.util.ArrayList;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index d9e9cc7..75a5976 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -15,6 +15,13 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
+import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
@@ -33,7 +40,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.model.PagedViewOrientedState;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.SystemUiProxy;
@@ -47,13 +53,6 @@
 import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
-import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
 /**
  * Utility class to handle window clip animation
  */
@@ -86,7 +85,7 @@
     private final Rect mTmpRect = new Rect();
     private final RectF mTmpRectF = new RectF();
     private final RectF mCurrentRectWithInsets = new RectF();
-    private PagedViewOrientedState mOrientedState;
+    private RecentsOrientedState mOrientedState;
     // Corner radius of windows, in pixels
     private final float mWindowCornerRadius;
     // Corner radius of windows when they're in overview mode.
@@ -105,7 +104,7 @@
     private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
     private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
 
-    public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) {
+    public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
         Resources res = context.getResources();
         mOrientedState = orientedState;
         mWindowCornerRadius = getWindowCornerRadius(res);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index 763f5be..e455939 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -78,21 +78,6 @@
         }
     }
 
-    public void onLayoutChanged() {
-        if (mParent == null) {
-            return;
-        }
-        setRotation(mParent.getPagedOrientationHandler().getDegreesRotated());
-    }
-
-    public void setRtl(boolean rtl) {
-        if (mIsRtl == rtl) {
-            return;
-        }
-        mIsRtl = rtl;
-        invalidate();
-    }
-
     public void setVisibilityAlpha(float alpha) {
         if (mVisibilityAlpha != alpha) {
             mVisibilityAlpha = alpha;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index e3b3102..a18f7ba 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -100,8 +100,8 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -122,6 +122,7 @@
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.systemui.plugins.ResourceProvider;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.recents.model.Task;
@@ -171,6 +172,8 @@
                 }
             };
 
+    protected final RecentsOrientedState mOrientationState = new RecentsOrientedState();
+
     private OrientationEventListener mOrientationListener;
     private int mPreviousRotation;
     protected RecentsAnimationController mRecentsAnimationController;
@@ -383,7 +386,7 @@
         mOrientationListener = new OrientationEventListener(getContext()) {
             @Override
             public void onOrientationChanged(int i) {
-                int rotation = RotationHelper.getRotationFromDegrees(i);
+                int rotation = RecentsOrientedState.getRotationForUserDegreesRotated(i);
                 if (mPreviousRotation != rotation) {
                     animateRecentsRotationInPlace(rotation);
                     if (rotation == 0) {
@@ -505,6 +508,29 @@
             mHasVisibleTaskData.delete(taskView.getTask().key.id);
             mTaskViewPool.recycle(taskView);
         }
+        updateTaskStartIndex(child);
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        child.setAlpha(mContentAlpha);
+        // RecentsView is set to RTL in the constructor when system is using LTR. Here we set the
+        // child direction back to match system settings.
+        child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
+        updateTaskStartIndex(child);
+    }
+
+    private void updateTaskStartIndex(View affectingView) {
+        if (!(affectingView instanceof TaskView) && !(affectingView instanceof ClearAllButton)) {
+            int childCount = getChildCount();
+
+            mTaskViewStartIndex = 0;
+            while (mTaskViewStartIndex < childCount
+                    && !(getChildAt(mTaskViewStartIndex) instanceof TaskView)) {
+                mTaskViewStartIndex++;
+            }
+        }
     }
 
     public boolean isTaskViewVisible(TaskView tv) {
@@ -661,7 +687,7 @@
             final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
             final Task task = tasks.get(i);
             final TaskView taskView = (TaskView) getChildAt(pageIndex);
-            taskView.bind(task, mLayoutRotation);
+            taskView.bind(task, mOrientationState);
         }
 
         if (mNextPage == INVALID_PAGE) {
@@ -710,11 +736,15 @@
         int currentIndex = indexOfChild(taskView);
         TaskView previousTask = getTaskViewAt(currentIndex - 1);
         TaskView nextTask = getTaskViewAt(currentIndex + 1);
+        float alpha = isTaskOverlayModal ? 0.0f : 1.0f;
         if (previousTask != null) {
-            previousTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE);
+            previousTask.animate().alpha(alpha)
+                    .translationX(isTaskOverlayModal ? previousTask.getWidth() / 2 : 0);
         }
         if (nextTask != null) {
-            nextTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE);
+            nextTask.animate().alpha(alpha)
+                    .translationX(isTaskOverlayModal ? -nextTask.getWidth() / 2 : 0);
+
         }
     }
 
@@ -819,7 +849,8 @@
         final int pageCount = getPageCount();
         for (int i = 0; i < pageCount; i++) {
             View page = getPageAt(i);
-            mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing);
+            mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page),
+                    mPageSpacing);
             ((PageCallbacks) page).onPageScroll(mScrollState);
         }
     }
@@ -973,7 +1004,7 @@
 
         AnimatorSet pa = setRecentsChangedOrientation(true);
         pa.addListener(AnimationSuccessListener.forRunnable(() -> {
-            updateLayoutRotation(newRotation);
+            setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
             mActivity.getDragLayer().recreateControllers();
             rotateAllChildTasks();
             setRecentsChangedOrientation(false).start();
@@ -999,8 +1030,7 @@
 
     private void rotateAllChildTasks() {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
-            taskView.setOverviewRotation(mLayoutRotation);
+            getTaskViewAt(i).setOrientationState(mOrientationState);
         }
     }
 
@@ -1041,7 +1071,7 @@
                     new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
                     false, true, false, false, new ActivityManager.TaskDescription(), 0,
                     new ComponentName("", ""), false);
-            taskView.bind(mTmpRunningTask, mLayoutRotation);
+            taskView.bind(mTmpRunningTask, mOrientationState);
         }
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -1207,7 +1237,7 @@
         /**
          * Updates linearInterpolation for the provided child position
          */
-        public void updateInterpolation(int childStart, int pageSpacing) {
+        public void updateInterpolation(float childStart, int pageSpacing) {
             float pageCenter = childStart + halfPageSize;
             float distanceFromScreenCenter = screenCenter - pageCenter;
             float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing;
@@ -1530,22 +1560,28 @@
         }
     }
 
-    @Override
     public void setLayoutRotation(int touchRotation, int displayRotation) {
-        if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
-            return;
+        if (mOrientationState.update(touchRotation, displayRotation)) {
+            mOrientationHandler = mOrientationState.getOrientationHandler();
+            mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
+            setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
+            mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
+            requestLayout();
         }
-
-        super.setLayoutRotation(touchRotation, displayRotation);
-        mClearAllButton.onLayoutChanged();
-        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
-        setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
     }
 
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        child.setAlpha(mContentAlpha);
+    public void disableMultipleLayoutRotations(boolean disable) {
+        mOrientationState.disableMultipleOrientations(disable);
+        mOrientationHandler = mOrientationState.getOrientationHandler();
+        requestLayout();
+    }
+
+    public RecentsOrientedState getPagedViewOrientedState() {
+        return mOrientationState;
+    }
+
+    public PagedOrientationHandler getPagedOrientationHandler() {
+        return mOrientationHandler;
     }
 
     @Nullable
@@ -1986,8 +2022,7 @@
     public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
         float degreesRotated;
         if (navbarRotation == 0) {
-            degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 :
-                    RotationHelper.getDegreesFromRotation(mLayoutRotation);
+            degreesRotated = mOrientationState.getTouchRotationDegrees();
         } else {
             degreesRotated = -navbarRotation;
         }
@@ -2001,14 +2036,14 @@
         return e -> {
             if (navbarRotation != 0
                     && !mOrientationState.areMultipleLayoutOrientationsDisabled()) {
-                RotationHelper.transformEventForNavBar(e, true);
+                mOrientationState.flipVertical(e);
                 super.onTouchEvent(e);
-                RotationHelper.transformEventForNavBar(e, false);
+                mOrientationState.flipVertical(e);
                 return;
             }
-            RotationHelper.transformEvent(-degreesRotated, e, true);
+            mOrientationState.transformEvent(-degreesRotated, e, true);
             super.onTouchEvent(e);
-            RotationHelper.transformEvent(-degreesRotated, e, false);
+            mOrientationState.transformEvent(-degreesRotated, e, false);
         };
     }
 
@@ -2056,37 +2091,11 @@
         }
     }
 
-    @Override
-    public void addView(View child, int index) {
-        // RecentsView is set to RTL in the constructor when system is using LTR. Here we set the
-        // child direction back to match system settings.
-        child.setLayoutDirection(
-                Utilities.isRtl(getResources())
-                        ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        super.addView(child, index);
-        if (isExtraCardView(child, index)) {
-            mTaskViewStartIndex++;
-        }
-    }
-
-    @Override
-    public void removeView(View view) {
-        if (isExtraCardView(view, indexOfChild(view))) {
-            mTaskViewStartIndex--;
-        }
-        super.removeView(view);
-    }
-
     @Nullable
     protected DepthController getDepthController() {
         return null;
     }
 
-    private boolean isExtraCardView(View view, int index) {
-        return !(view instanceof TaskView) && !(view instanceof ClearAllButton)
-                && index <= mTaskViewStartIndex;
-    }
-
     /**
      * Used to register callbacks for when our empty message state changes.
      *
@@ -2131,13 +2140,15 @@
                 && SysUINavigationMode.removeShelfFromOverview(mActivity)) {
             mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view);
             if (mActionsView != null) {
-                Rect rect = new Rect();
-                getTaskSize(rect);
                 InsettableFrameLayout.LayoutParams layoutParams =
-                        new InsettableFrameLayout.LayoutParams(rect.width(),
+                        new InsettableFrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                                 getResources().getDimensionPixelSize(
                                         R.dimen.overview_actions_height));
                 layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+                int margin = getResources().getDimensionPixelSize(
+                        R.dimen.overview_actions_horizontal_margin);
+                layoutParams.setMarginStart(margin);
+                layoutParams.setMarginEnd(margin);
                 mActionsView.setLayoutParams(layoutParams);
                 showActionsView();
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 7010f9a..470b720 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -64,7 +64,6 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -79,6 +78,7 @@
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.TaskCornerRadius;
 import com.android.quickstep.views.RecentsView.PageCallbacks;
 import com.android.quickstep.views.RecentsView.ScrollState;
@@ -119,19 +119,6 @@
     private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
             Collections.singletonList(new Rect());
 
-    public static final FloatProperty<TaskView> FULLSCREEN_PROGRESS =
-            new FloatProperty<TaskView>("fullscreenProgress") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setFullscreenProgress(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mFullscreenProgress;
-                }
-            };
-
     private static final FloatProperty<TaskView> FOCUS_TRANSITION =
             new FloatProperty<TaskView>("focusTransition") {
                 @Override
@@ -180,6 +167,7 @@
     private float mStableAlpha = 1;
 
     private boolean mShowScreenshot;
+    private boolean mRunningModalAnimation = false;
 
     // The current background requests to load the task thumbnail and icon
     private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
@@ -262,17 +250,39 @@
     /** Updates UI based on whether the task is modal. */
     public void updateUiForModalTask() {
         boolean isOverlayModal = isTaskOverlayModal();
+        mRunningModalAnimation = true;
         if (getRecentsView() != null) {
             getRecentsView().updateUiForModalTask(this, isOverlayModal);
         }
-        // Hide footers when overlay is modal.
+
+        // Hides footers and icon when overlay is modal.
         if (isOverlayModal) {
             for (FooterWrapper footer : mFooters) {
                 if (footer != null) {
                     footer.animateHide();
                 }
             }
+            mIconView.animate().alpha(0.0f);
+        } else {
+            mIconView.animate().alpha(1.0f);
         }
+
+        // Sets animations for modal UI. We will remove the margins to zoom in the snapshot.
+        float topMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
+        float bottomMargin =
+                getResources().getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions);
+        float newHeight = mSnapshotView.getHeight() + topMargin + bottomMargin;
+        float scale = isOverlayModal ? newHeight / mSnapshotView.getHeight() : 1.0f;
+        float centerDifference = (bottomMargin - topMargin) / 2;
+        float translationY = isOverlayModal ? centerDifference : 0;
+        this.animate().scaleX(scale).scaleY(scale).translationY(translationY)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        setCurveScale(scale);
+                        mRunningModalAnimation = false;
+                    }
+                });
     }
 
     public TaskMenuView getMenuView() {
@@ -286,11 +296,11 @@
     /**
      * Updates this task view to the given {@param task}.
      */
-    public void bind(Task task, int recentsRotation) {
+    public void bind(Task task, RecentsOrientedState orientedState) {
         cancelPendingLoadTasks();
         mTask = task;
         mSnapshotView.bind(task);
-        setOverviewRotation(recentsRotation);
+        setOrientationState(orientedState);
     }
 
     public Task getTask() {
@@ -459,14 +469,15 @@
         }
     }
 
-    void setOverviewRotation(int iconRotation) {
-        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
+    public void setOrientationState(RecentsOrientedState orientationState) {
+        int iconRotation = orientationState.getTouchRotation();
+        PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources());
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext());
         int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
         LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        int rotation = RotationHelper.getDegreesFromRotation(iconRotation);
+        int rotation = orientationState.getTouchRotationDegrees();
         switch (iconRotation) {
             case Surface.ROTATION_90:
                 iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
@@ -579,11 +590,15 @@
 
     @Override
     public void onPageScroll(ScrollState scrollState) {
+        // Don't do anything if it's modal.
+        if (mRunningModalAnimation || isTaskOverlayModal()) {
+            return;
+        }
+
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
         float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
                 curveInterpolation);
-
         mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
         setCurveScale(curveScaleForCurveInterpolation);
 
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index dcc85d5..9a61165 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -17,6 +17,7 @@
 <resources>
 
     <dimen name="task_thumbnail_top_margin">24dp</dimen>
+    <dimen name="task_thumbnail_bottom_margin_with_actions">44dp</dimen>
     <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
     <!-- For screens without rounded corners -->
@@ -24,6 +25,7 @@
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
     <dimen name="overview_actions_height">110dp</dimen>
+    <dimen name="overview_actions_horizontal_margin">16dp</dimen>
 
     <dimen name="recents_page_spacing">10dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index abdff0d..48e25bd 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -17,14 +17,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
@@ -32,13 +25,13 @@
 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 android.view.View;
 
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.accessibility.SystemActions;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
@@ -48,12 +41,14 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 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.util.ShelfPeekAnim;
@@ -70,7 +65,6 @@
         implements NavigationModeChangeListener {
 
     private DepthController mDepthController = new DepthController(this);
-    protected SystemActions mSystemActions;
 
     /**
      * Reusable command for applying the back button alpha on the background thread.
@@ -86,48 +80,8 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mSystemActions = new SystemActions(this);
 
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
-
-        if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
-            getStateManager().addStateListener(new LauncherStateManager.StateListener() {
-                @Override
-                public void onStateTransitionStart(LauncherState toState) { }
-
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
-                            .get(BaseQuickstepLauncher.this).getMode().hasGestures;
-                    LauncherState prevState = getStateManager().getLastState();
-
-                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
-                            && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
-                            <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
-                        getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-                        getStateManager().removeStateListener(this);
-                    }
-                }
-            });
-        }
-
-        if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
-            getStateManager().addStateListener(new LauncherStateManager.StateListener() {
-                @Override
-                public void onStateTransitionStart(LauncherState toState) { }
-
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    LauncherState prevState = getStateManager().getLastState();
-
-                    if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
-                            <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
-                        getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-                        getStateManager().removeStateListener(this);
-                    }
-                }
-            });
-        }
     }
 
     @Override
@@ -142,12 +96,6 @@
     }
 
     @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-        mSystemActions.onActivityResult(requestCode);
-    }
-
-    @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
@@ -210,15 +158,6 @@
             // removes the task itself.
             startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
         }
-
-        // Register all system actions once they are available
-        mSystemActions.register();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mSystemActions.unregister();
     }
 
     @Override
@@ -262,6 +201,12 @@
     }
 
     @Override
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
+            LauncherStateManager stateManager) {
+        return new QuickstepOnboardingPrefs(this, sharedPrefs, stateManager);
+    }
+
+    @Override
     protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
         if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
             PagedOrientationHandler layoutVertical =
diff --git a/quickstep/src/com/android/launcher3/accessibility/SystemActions.java b/quickstep/src/com/android/launcher3/accessibility/SystemActions.java
deleted file mode 100644
index 669877f..0000000
--- a/quickstep/src/com/android/launcher3/accessibility/SystemActions.java
+++ /dev/null
@@ -1,90 +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.accessibility;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.drawable.Icon;
-import android.view.accessibility.AccessibilityManager;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.R;
-
-/**
- * Manages the launcher system actions presented to accessibility services.
- */
-public class SystemActions {
-
-    /**
-     * System Action ID to show all apps.  This ID should follow the ones in
-     * com.android.systemui.accessibility.SystemActions.
-     */
-    private static final int SYSTEM_ACTION_ID_ALL_APPS = 100;
-
-    private Launcher mLauncher;
-    private AccessibilityManager mAccessibilityManager;
-    private RemoteAction mAllAppsAction;
-    private boolean mRegistered;
-
-    public SystemActions(Launcher launcher) {
-        mLauncher = launcher;
-        mAccessibilityManager = (AccessibilityManager) launcher.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
-        mAllAppsAction = new RemoteAction(
-                Icon.createWithResource(launcher, R.drawable.ic_apps),
-                launcher.getString(R.string.all_apps_label),
-                launcher.getString(R.string.all_apps_label),
-                launcher.createPendingResult(SYSTEM_ACTION_ID_ALL_APPS, new Intent(),
-                        0 /* flags */));
-    }
-
-    public void register() {
-        if (mRegistered) {
-            return;
-        }
-        mAccessibilityManager.registerSystemAction(mAllAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
-        mRegistered = true;
-    }
-
-    public void unregister() {
-        if (!mRegistered) {
-            return;
-        }
-        mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
-        mRegistered = false;
-    }
-
-    public void onActivityResult(int requestCode) {
-        if (requestCode == SYSTEM_ACTION_ID_ALL_APPS) {
-            showAllApps();
-        }
-    }
-
-    private void showAllApps() {
-        LauncherStateManager stateManager = mLauncher.getStateManager();
-        stateManager.goToState(NORMAL);
-        stateManager.goToState(ALL_APPS);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 92c8573..2181aa8 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -45,8 +45,8 @@
 import androidx.annotation.MainThread;
 
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.RemoteActionShortcut;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.MainThreadInitializedObject;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 123c988..0f45196 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -62,10 +62,13 @@
 
     @Override
     public void setState(@NonNull LauncherState state) {
-        ScaleAndTranslation scaleAndTranslation = state
-                .getOverviewScaleAndTranslation(mLauncher);
+        ScaleAndTranslation scaleAndTranslation = state.getOverviewScaleAndTranslation(mLauncher);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
         SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
-        mRecentsView.setTranslationX(scaleAndTranslation.translationX);
+        mRecentsView.setTranslationX(translationX);
         mRecentsView.setTranslationY(scaleAndTranslation.translationY);
 
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
@@ -96,9 +99,13 @@
     void setStateWithAnimationInternal(@NonNull final LauncherState toState,
             @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
         ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
         setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale,
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
-        setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, scaleAndTranslation.translationX,
+        setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, translationX,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
         setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
deleted file mode 100644
index c7cce0b..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
+++ /dev/null
@@ -1,48 +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.uioverrides;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Size;
-import android.view.View;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
-import com.android.systemui.shared.system.SurfaceViewRequestReceiver;
-
-/** Render preview using surface view. */
-public class PreviewSurfaceRenderer {
-
-    /** Handle a received surface view request. */
-    public static void render(Context context, Bundle bundle) {
-        String gridName = bundle.getString("name");
-        bundle.remove("name");
-        if (gridName == null) {
-            gridName = InvariantDeviceProfile.getCurrentGridName(context);
-        }
-        final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
-
-        MAIN_EXECUTOR.execute(() -> {
-            View view = new LauncherPreviewRenderer(context, idp).getRenderedView();
-            new SurfaceViewRequestReceiver().onReceive(context, bundle, view,
-                    new Size(view.getMeasuredWidth(), view.getMeasuredHeight()));
-        });
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 818d836..a4861dc 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -70,10 +70,10 @@
     }
 
     /**
-     * Returns true if the given input consumer is in the hierarchy of this input consumer.
+     * Returns the active input consumer is in the hierarchy of this input consumer.
      */
-    default boolean isInConsumerHierarchy(InputConsumer candidate) {
-        return this == candidate;
+    default InputConsumer getActiveConsumerInHierarchy() {
+        return this;
     }
 
     /**
@@ -96,15 +96,15 @@
     }
 
     default String getName() {
-        String name = "";
+        StringBuilder name = new StringBuilder();
         for (int i = 0; i < NAMES.length; i++) {
             if ((getType() & (1 << i)) != 0) {
                 if (name.length() > 0) {
-                    name += ":";
+                    name.append(":");
                 }
-                name += NAMES[i];
+                name.append(NAMES[i]);
             }
         }
-        return name;
+        return name.toString();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
index 3e9872a..7638541 100644
--- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -21,7 +21,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
-import com.android.launcher3.AppInfo;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
 /**
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 3e73f49..495c092 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -22,6 +22,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
+
 import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -33,8 +35,8 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.util.RecentsOrientedState.SurfaceRotation;
 
 import java.io.PrintWriter;
 
@@ -52,15 +54,30 @@
     private static final boolean DEBUG = false;
     private static final int MAX_ORIENTATIONS = 4;
 
+    private final Matrix mTmpMatrix = new Matrix();
+    private final float[] mTmpPoint = new float[2];
+
     private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
     private final RectF mAssistantLeftRegion = new RectF();
     private final RectF mAssistantRightRegion = new RectF();
-    private int mCurrentRotation;
+    private int mCurrentDisplayRotation;
     private boolean mEnableMultipleRegions;
     private Resources mResources;
     private OrientationRectF mLastRectTouched;
     private SysUINavigationMode.Mode mMode;
     private QuickStepContractInfo mContractInfo;
+
+    /**
+     * Represents if we're currently in a swipe "session" of sorts. If value is -1, then user
+     * has not tapped on an active nav region. Otherwise it will be the rotation of the display
+     * when the user first interacted with the active nav bar region.
+     * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
+     * called - usually from a timeout or if user starts interacting w/ the foreground app.
+     *
+     * This is different than {@link #mLastRectTouched} as it can get reset by the system whereas
+     * the rect is purely used for tracking touch interactions and usually this "session" will
+     * outlast the touch interaction.
+     */
     private int mQuickStepStartingRotation = -1;
 
     /** For testability */
@@ -68,6 +85,7 @@
         float getWindowCornerRadius();
     }
 
+
     OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode,
             QuickStepContractInfo contractInfo) {
         mResources = resources;
@@ -92,20 +110,21 @@
      * @see #enableMultipleRegions(boolean, DefaultDisplay.Info)
      */
     void createOrAddTouchRegion(DefaultDisplay.Info info) {
-        mCurrentRotation = info.rotation;
-        if (mQuickStepStartingRotation > -1 && mCurrentRotation == mQuickStepStartingRotation) {
-            // Ignore nav bars in other rotations except for the one we started out in
+        mCurrentDisplayRotation = info.rotation;
+        if (mQuickStepStartingRotation > -1
+                && mCurrentDisplayRotation == mQuickStepStartingRotation) {
+            // User already was swiping and the current screen is same rotation as the starting one
+            // Remove active nav bars in other rotations except for the one we started out in
             resetSwipeRegions(info);
             return;
         }
-
-        OrientationRectF region = mSwipeTouchRegions.get(mCurrentRotation);
+        OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplayRotation);
         if (region != null) {
             return;
         }
 
         if (mEnableMultipleRegions) {
-            mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(info));
+            mSwipeTouchRegions.put(mCurrentDisplayRotation, createRegionForDisplay(info));
         } else {
             resetSwipeRegions(info);
         }
@@ -123,12 +142,6 @@
         if (!enableMultipleRegions) {
             mQuickStepStartingRotation = -1;
             resetSwipeRegions(info);
-        } else {
-            if (mLastRectTouched != null) {
-                // mLastRectTouched can be null if gesture type is changed (ex. from settings)
-                // but nav bar hasn't been interacted with yet.
-                mQuickStepStartingRotation = mLastRectTouched.mRotation;
-            }
         }
     }
 
@@ -140,17 +153,25 @@
      */
     private void resetSwipeRegions(DefaultDisplay.Info region) {
         if (DEBUG) {
-            Log.d(TAG, "clearing all regions except rotation: " + mCurrentRotation);
+            Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplayRotation);
         }
 
-        mCurrentRotation = region.rotation;
+        mCurrentDisplayRotation = region.rotation;
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
         mSwipeTouchRegions.clear();
-        mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(region));
+        mSwipeTouchRegions.put(mCurrentDisplayRotation,
+                regionToKeep != null ? regionToKeep : createRegionForDisplay(region));
+    }
+
+    private void resetSwipeRegions() {
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+        mSwipeTouchRegions.clear();
+        mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
     }
 
     private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) {
         if (DEBUG) {
-            Log.d(TAG, "creating rotation region for: " + mCurrentRotation);
+            Log.d(TAG, "creating rotation region for: " + mCurrentDisplayRotation);
         }
 
         Point size = display.realSize;
@@ -220,6 +241,10 @@
         }
     }
 
+    int getQuickStepStartingRotation() {
+        return mQuickStepStartingRotation;
+    }
+
     public void transform(MotionEvent event) {
         int eventAction = event.getActionMasked();
         switch (eventAction) {
@@ -252,6 +277,11 @@
                     }
                     if (rect.applyTransform(event, false)) {
                         mLastRectTouched = rect;
+                        if (mCurrentDisplayRotation == mLastRectTouched.mRotation) {
+                            // Start a touch session for the default nav region for the display
+                            mQuickStepStartingRotation = mLastRectTouched.mRotation;
+                            resetSwipeRegions();
+                        }
                         if (DEBUG) {
                             Log.d(TAG, "set active region: " + rect);
                         }
@@ -304,42 +334,46 @@
         }
 
         boolean applyTransform(MotionEvent event, boolean forceTransform) {
-            // TODO(b/149658423): See if we can use RotationHelper.getRotationMatrix here
-            MotionEvent tmp = MotionEvent.obtain(event);
-            Matrix outMatrix = new Matrix();
-            int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation);
-            switch (delta) {
-                case Surface.ROTATION_0:
-                    outMatrix.reset();
-                    break;
-                case Surface.ROTATION_90:
-                    outMatrix.setRotate(270);
-                    outMatrix.postTranslate(0, mHeight);
-                    break;
-                case Surface.ROTATION_180:
-                    outMatrix.setRotate(180);
-                    outMatrix.postTranslate(mHeight, mWidth);
-                    break;
-                case Surface.ROTATION_270:
-                    outMatrix.setRotate(90);
-                    outMatrix.postTranslate(mWidth, 0);
-                    break;
+            mTmpMatrix.reset();
+            postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
+                    mHeight, mWidth, mTmpMatrix);
+            if (forceTransform) {
+                if (DEBUG) {
+                    Log.d(TAG, "Transforming rotation due to forceTransform, "
+                            + "mCurrentRotation: " + mCurrentDisplayRotation
+                            + "mRotation: " + mRotation);
+                }
+                event.transform(mTmpMatrix);
+                return true;
             }
+            mTmpPoint[0] = event.getX();
+            mTmpPoint[1] = event.getY();
+            mTmpMatrix.mapPoints(mTmpPoint);
 
-            tmp.transform(outMatrix);
             if (DEBUG) {
                 Log.d(TAG, "original: " + event.getX() + ", " + event.getY()
-                                + " new: " + tmp.getX() + ", " + tmp.getY()
+                                + " new: " + mTmpPoint[0] + ", " + mTmpPoint[1]
                                 + " rect: " + this + " forceTransform: " + forceTransform
-                                + " contains: " + contains(tmp.getX(), tmp.getY()));
+                                + " contains: " + contains(mTmpPoint[0], mTmpPoint[1]));
             }
 
-            if (forceTransform || contains(tmp.getX(), tmp.getY())) {
-                event.transform(outMatrix);
-                tmp.recycle();
+            if (contains(mTmpPoint[0], mTmpPoint[1])) {
+                event.transform(mTmpMatrix);
                 return true;
             }
             return false;
         }
     }
+
+    /**
+     * @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
+     * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
+     * A value of 0 means no rotation has been applied
+     */
+    @SurfaceRotation
+    private static int deltaRotation(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 9edc86e..231ee72 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -40,6 +40,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 /**
  * Class to keep track of the current overview component based off user preferences and app updates
@@ -57,6 +58,9 @@
     private final Intent mMyHomeIntent;
     private final Intent mFallbackIntent;
     private final SparseIntArray mConfigChangesMap = new SparseIntArray();
+
+    private Consumer<Boolean> mOverviewChangeListener = b -> { };
+
     private String mUpdateRegisteredPackage;
     private BaseActivityInterface mActivityInterface;
     private Intent mOverviewIntent;
@@ -64,10 +68,10 @@
     private boolean mIsDefaultHome;
     private boolean mIsHomeDisabled;
 
+
     public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
         mContext = context;
         mDeviceState = deviceState;
-
         mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -95,6 +99,13 @@
         updateOverviewTargets();
     }
 
+    /**
+     * Sets a listener for changes in {@link #isHomeAndOverviewSame()}
+     */
+    public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
+        mOverviewChangeListener = overviewChangeListener;
+    }
+
     public void onSystemUiStateChanged() {
         if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
             updateOverviewTargets();
@@ -159,6 +170,7 @@
                         ACTION_PACKAGE_REMOVED));
             }
         }
+        mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 10f9feb..b0ce8e6 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -124,6 +124,17 @@
     }
 
     @Override
+    public void onRecentTaskListUpdated() {
+        // In some cases immediately after booting, the tasks in the system recent task list may be
+        // loaded, but not in the active task hierarchy in the system.  These tasks are displayed in 
+        // overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
+        // callback (those are for changes to the active tasks), but the task list is still updated,
+        // so we should also invalidate the change id to ensure we load a new list instead of 
+        // reusing a stale list.
+        mChangeId++;
+    }
+
+    @Override
     public void onTaskRemoved(int taskId) {
         mTasks = loadTasksInBackground(Integer.MAX_VALUE, false);
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 566a701..7d568a4 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -91,9 +91,6 @@
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
             Rect homeContentInsets, Rect minimizedHomeBounds) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationStart");
-        }
         RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
                 wallpaperTargets, homeContentInsets, minimizedHomeBounds);
         mController = new RecentsAnimationController(animationController,
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 268e564..5ece2d7 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -22,13 +22,13 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
-import android.annotation.NonNull;
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.util.Preconditions;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 491c611..6dbf2b0 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,6 +17,7 @@
 
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -73,6 +74,8 @@
         NavigationModeChangeListener,
         DefaultDisplay.DisplayInfoChangeListener {
 
+    private static final String TAG = "RecentsAnimationDeviceState";
+
     private final Context mContext;
     private final SysUINavigationMode mSysUiNavMode;
     private final DefaultDisplay mDefaultDisplay;
@@ -95,6 +98,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+                Log.d(TAG, "User Unlocked Broadcast Received");
                 mIsUserUnlocked = true;
                 notifyUserUnlocked();
             }
@@ -120,7 +124,6 @@
     private Runnable mOnDestroyFrozenTaskRunnable;
 
     public RecentsAnimationDeviceState(Context context) {
-        final ContentResolver resolver = context.getContentResolver();
         mContext = context;
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
         mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
@@ -511,8 +514,16 @@
         mOrientationTouchTransformer.transform(event);
     }
 
-    public void enableMultipleRegions(boolean enable) {
+    void enableMultipleRegions(boolean enable) {
         mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+        if (enable) {
+            UI_HELPER_EXECUTOR.execute(() -> {
+                int quickStepStartingRotation =
+                        mOrientationTouchTransformer.getQuickStepStartingRotation();
+                SystemUiProxy.INSTANCE.get(mContext)
+                        .onQuickSwitchToNewTask(quickStepStartingRotation);
+            });
+        }
     }
 
     public int getCurrentActiveRotation() {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index f5aaf1d..20d133c 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -334,6 +334,7 @@
         }
     }
 
+    @Override
     public void onQuickSwitchToNewTask(int rotation) {
         if (mSystemUiProxy != null) {
             try {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index b04a1ae..bbca568 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -53,9 +53,6 @@
     @UiThread
     public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
             Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_START_FROM_RECENTS, "startRecentsAnimation");
-        }
         // Notify if recents animation is still running
         if (mController != null) {
             String msg = "New recents animation started before old animation completed";
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java
index c9ee1e2..297c287 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java
@@ -35,7 +35,7 @@
     @Override
     void transitToController() {
         super.transitToController();
-        mHandCoachingAnimation.maybeStartLoopedAnimation(mTutorialTypeInfo.get().getTutorialType());
+        mHandCoachingAnimation.startLoopedAnimation(mTutorialTypeInfo.get().getTutorialType());
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java
index d03811d..d7c10b0 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java
@@ -38,8 +38,6 @@
     private final ImageView mHandCoachingView;
     private final AnimatedVectorDrawable mGestureAnimation;
 
-    private boolean mIsAnimationPlayed = false;
-
     BackGestureTutorialHandAnimation(Context context, View rootView) {
         mHandCoachingView = rootView.findViewById(
                 R.id.back_gesture_tutorial_fragment_hand_coaching);
@@ -47,20 +45,15 @@
                 R.drawable.back_gesture);
     }
 
-    boolean isRunning() {
-        return mGestureAnimation.isRunning();
-    }
-
     /**
-     * Starts animation if the playground is launched for the first time.
+     * [Re]starts animation for the given tutorial.
      */
-    void maybeStartLoopedAnimation(TutorialType tutorialType) {
-        if (isRunning() || mIsAnimationPlayed) {
-            return;
+    void startLoopedAnimation(TutorialType tutorialType) {
+        if (mGestureAnimation.isRunning()) {
+            stop();
         }
 
-        mIsAnimationPlayed = true;
-        clearAnimationCallbacks();
+        mGestureAnimation.clearAnimationCallbacks();
         mGestureAnimation.registerAnimationCallback(
                 new Animatable2.AnimationCallback() {
                     @Override
@@ -78,17 +71,11 @@
         float rotationY = tutorialType == TutorialType.LEFT_EDGE_BACK_NAVIGATION ? 180f : 0f;
         mHandCoachingView.setRotationY(rotationY);
         mHandCoachingView.setImageDrawable(mGestureAnimation);
-        mHandCoachingView.postDelayed(() -> mGestureAnimation.start(),
-                ANIMATION_START_DELAY.toMillis());
-    }
-
-    private void clearAnimationCallbacks() {
-        mGestureAnimation.clearAnimationCallbacks();
+        mHandCoachingView.postDelayed(mGestureAnimation::start, ANIMATION_START_DELAY.toMillis());
     }
 
     void stop() {
-        mIsAnimationPlayed = false;
-        clearAnimationCallbacks();
+        mGestureAnimation.clearAnimationCallbacks();
         mGestureAnimation.stop();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 58bb980..ac2200d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -23,16 +23,16 @@
 
 import android.content.Context;
 
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.IntSparseArrayMap;
 
 import java.util.ArrayList;
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
new file mode 100644
index 0000000..ab2484d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -0,0 +1,96 @@
+/*
+ * 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.quickstep.util;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.content.SharedPreferences;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.StateListener;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
+ */
+public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
+
+    public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs,
+            LauncherStateManager stateManager) {
+        super(launcher, sharedPrefs, stateManager);
+
+        if (!getBoolean(HOME_BOUNCE_SEEN)) {
+            mStateManager.addStateListener(new StateListener() {
+                @Override
+                public void onStateTransitionStart(LauncherState toState) { }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
+                            .get(mLauncher).getMode().hasGestures;
+                    LauncherState prevState = mStateManager.getLastState();
+
+                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+                            && finalState == ALL_APPS && prevState == NORMAL) ||
+                            hasReachedMaxCount(HOME_BOUNCE_COUNT))) {
+                        mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+                        mStateManager.removeStateListener(this);
+                    }
+                }
+            });
+        }
+
+        if (!getBoolean(SHELF_BOUNCE_SEEN)) {
+            mStateManager.addStateListener(new StateListener() {
+                @Override
+                public void onStateTransitionStart(LauncherState toState) { }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    LauncherState prevState = mStateManager.getLastState();
+
+                    if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
+                            hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
+                        mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+                        mStateManager.removeStateListener(this);
+                    }
+                }
+            });
+        }
+
+        if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
+            mStateManager.addStateListener(new StateListener() {
+                @Override
+                public void onStateTransitionStart(LauncherState toState) { }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    if (finalState == ALL_APPS) {
+                        if (incrementEventCount(ALL_APPS_COUNT)) {
+                            mStateManager.removeStateListener(this);
+                            mLauncher.getScrimView().updateDragHandleVisibility();
+                        }
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
new file mode 100644
index 0000000..f72e458
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -0,0 +1,217 @@
+/*
+ * 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.quickstep.util;
+
+import static android.hardware.camera2.params.OutputConfiguration.ROTATION_180;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import androidx.annotation.IntDef;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.touch.PortraitPagedViewHandler;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Container to hold orientation/rotation related information for Launcher.
+ * This is not meant to be an abstraction layer for applying different functionality between
+ * the different orientation/rotations. For that see {@link PagedOrientationHandler}
+ *
+ * This class has initial default state assuming the device and foreground app have
+ * no ({@link Surface#ROTATION_0} rotation.
+ */
+public final class RecentsOrientedState {
+
+    @Retention(SOURCE)
+    @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
+    public @interface SurfaceRotation {}
+
+    private PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT;
+
+    private @SurfaceRotation int mTouchRotation = ROTATION_0;
+    private @SurfaceRotation int mDisplayRotation = ROTATION_0;
+    /**
+     * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake
+     * launcher orientations.
+     */
+    private boolean mDisableMultipleOrientations;
+
+    private final Matrix mTmpMatrix = new Matrix();
+    private final Matrix mTmpInverseMatrix = new Matrix();
+
+    /**
+     * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
+     * @param touchRotation The rotation the nav bar region that is touched is in
+     * @param displayRotation Rotation of the display/device
+     *
+     * @return true if there was any change in the internal state as a result of this call,
+     *         false otherwise
+     */
+    public boolean update(
+            @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
+        if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
+            return false;
+        }
+        if (mDisableMultipleOrientations) {
+            return false;
+        }
+        if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation) {
+            return false;
+        }
+
+        mDisplayRotation = displayRotation;
+        mTouchRotation = touchRotation;
+        if (mTouchRotation == ROTATION_90) {
+            mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
+        } else if (mTouchRotation == ROTATION_270) {
+            mOrientationHandler = PagedOrientationHandler.SEASCAPE;
+        } else {
+            mOrientationHandler = PagedOrientationHandler.PORTRAIT;
+        }
+        return true;
+    }
+
+    public boolean areMultipleLayoutOrientationsDisabled() {
+        return mDisableMultipleOrientations;
+    }
+
+    /**
+     * Setting this preference will render future calls to {@link #update(int, int)} as a no-op.
+     */
+    public void disableMultipleOrientations(boolean disable) {
+        mDisableMultipleOrientations = disable;
+        if (disable) {
+            mDisplayRotation = mTouchRotation = ROTATION_0;
+            mOrientationHandler = PagedOrientationHandler.PORTRAIT;
+        }
+    }
+
+    @SurfaceRotation
+    public int getDisplayRotation() {
+        return mDisplayRotation;
+    }
+
+    @SurfaceRotation
+    public int getTouchRotation() {
+        return mTouchRotation;
+    }
+
+    public int getTouchRotationDegrees() {
+        switch (mTouchRotation) {
+            case ROTATION_90:
+                return 90;
+            case ROTATION_180:
+                return 180;
+            case ROTATION_270:
+                return 270;
+            case ROTATION_0:
+            default:
+                return 0;
+        }
+    }
+
+    public PagedOrientationHandler getOrientationHandler() {
+        return mOrientationHandler;
+    }
+
+    /**
+     * For landscape, since the navbar is already in a vertical position, we don't have to do any
+     * rotations as the change in Y coordinate is what is read. We only flip the sign of the
+     * y coordinate to make it match existing behavior of swipe to the top to go previous
+     */
+    public void flipVertical(MotionEvent ev) {
+        mTmpMatrix.setScale(1, -1);
+        ev.transform(mTmpMatrix);
+    }
+
+    /**
+     * Creates a matrix to transform the given motion event specified by degrees.
+     * If inverse is {@code true}, the inverse of that matrix will be applied
+     */
+    public void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
+        mTmpMatrix.setRotate(inverse ? -degrees : degrees);
+        ev.transform(mTmpMatrix);
+
+        // TODO: Add scaling back in based on degrees
+        /*
+        if (getWidth() > 0 && getHeight() > 0) {
+            float scale = ((float) getWidth()) / getHeight();
+            transform.postScale(scale, 1 / scale);
+        }
+        */
+    }
+
+    public void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
+        mTmpMatrix.reset();
+        postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
+        mTmpMatrix.mapRect(src);
+    }
+
+    public void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
+        mTmpMatrix.reset();
+        postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
+        mTmpMatrix.invert(mTmpInverseMatrix);
+        mTmpInverseMatrix.mapRect(src);
+    }
+
+    @SurfaceRotation
+    public static int getRotationForUserDegreesRotated(float degrees) {
+        int threshold = 70;
+        if (degrees >= (360 - threshold) || degrees < (threshold)) {
+            return ROTATION_0;
+        } else if (degrees < (90 + threshold)) {
+            return ROTATION_270;
+        } else if (degrees < 180 + threshold) {
+            return ROTATION_180;
+        } else {
+            return ROTATION_90;
+        }
+    }
+
+    /**
+     * Posts the transformation on the matrix representing the provided display rotation
+     */
+    public static void postDisplayRotation(@SurfaceRotation int displayRotation,
+            float screenWidth, float screenHeight, Matrix out) {
+        switch (displayRotation) {
+            case ROTATION_0:
+                return;
+            case ROTATION_90:
+                out.postRotate(270);
+                out.postTranslate(0, screenWidth);
+                break;
+            case ROTATION_180:
+                out.postRotate(180);
+                out.postTranslate(screenHeight, screenWidth);
+                break;
+            case ROTATION_270:
+                out.postRotate(90);
+                out.postTranslate(screenHeight, 0);
+                break;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index c2ccd90..f5498c9 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
@@ -75,6 +76,7 @@
     private final float mRadius;
     private final int mMaxScrimAlpha;
     private final Paint mPaint;
+    private final OnboardingPrefs mOnboardingPrefs;
 
     // Mid point where the alpha changes
     private int mMidAlpha;
@@ -100,6 +102,7 @@
     private boolean mRemainingScreenPathValid = false;
 
     private Mode mSysUINavigationMode;
+    private boolean mIsTwoZoneSwipeModel;
 
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -108,6 +111,7 @@
         mEndAlpha = Color.alpha(mEndScrim);
         mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mOnboardingPrefs = mLauncher.getOnboardingPrefs();
 
         // Just assume the easiest UI for now, until we have the proper layout information.
         mDrawingFlatColor = true;
@@ -140,9 +144,11 @@
             // Show the shelf more quickly before reaching overview progress.
             mBeforeMidProgressColorInterpolator = ACCEL_2;
             mAfterMidProgressColorInterpolator = ACCEL;
+            mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
         } else {
             mBeforeMidProgressColorInterpolator = ACCEL;
             mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
+            mIsTwoZoneSwipeModel = false;
         }
     }
 
@@ -236,9 +242,9 @@
 
     @Override
     protected boolean shouldDragHandleBeVisible() {
-        boolean twoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
-                && SysUINavigationMode.removeShelfFromOverview(mLauncher);
-        return twoZoneSwipeModel || super.shouldDragHandleBeVisible();
+        boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
+                && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
+        return needsAllAppsEdu || super.shouldDragHandleBeVisible();
     }
 
     @Override
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index f8f22a1..8ecd88a 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -48,9 +48,9 @@
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Background;
 import com.android.launcher3.testcomponent.ListViewService;
 import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory;
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index f64b2d9..08e1c98 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -20,7 +20,7 @@
     android:gravity="center">
 
     <TextView
-        style="@style/PrimaryMediumText"
+        style="@style/PrimaryHeadline"
         android:textColor="?attr/workProfileOverlayTextColor"
         android:id="@+id/work_apps_paused_title"
         android:layout_width="wrap_content"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 5a15ec6..e946835 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -118,6 +118,8 @@
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
         <attr name="dbFile" format="string" />
+        <!-- numAllAppsColumns defaults to numColumns, if not specified -->
+        <attr name="numAllAppsColumns" format="integer" />
         <attr name="defaultLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
     </declare-styleable>
@@ -133,6 +135,12 @@
         <attr name="iconTextSize" format="float" />
         <!-- If true, this display option is used to determine the default grid -->
         <attr name="canBeDefault" format="boolean" />
+
+        <!-- The following values are only enabled if grid is supported. -->
+        <!-- allAppsIconSize defaults to iconSize, if not specified -->
+        <attr name="allAppsIconSize" format="float" />
+        <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
+        <attr name="allAppsIconTextSize" format="float" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/res/values/config.xml b/res/values/config.xml
index ef67613..0657b86 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -131,8 +131,8 @@
     <item name="dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.5</item>
     <item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">1500</item>
 
-    <item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.75</item>
-    <item name="horizontal_spring_stiffness" type="dimen" format="float">200</item>
+    <item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.8</item>
+    <item name="horizontal_spring_stiffness" type="dimen" format="float">400</item>
 
     <item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
     <item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ac04262..a9ce24c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -343,6 +343,10 @@
     <string name="work_apps_paused_title">Work profile is paused</string>
     <!--- body shown when user opens work apps tab while work apps are paused -->
     <string name="work_apps_paused_body">Work apps can\’t send you notifications, use your battery, or access your location</string>
+    <!-- content description for paused work apps list -->
+    <string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
+
+
 
     <!-- A tip shown pointing at work toggle -->
     <string name="work_switch_tip">Pause work apps and notifications</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index bc6ab45..fcc651b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -226,6 +226,9 @@
 
     <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
     <style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
+    <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+        <item name="android:textStyle">bold</item>
+    </style>
 
     <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
 
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index 7c7e73c..bbc62e9 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_MODULE := LauncherRoboTests
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := \
     androidx.test.runner \
@@ -47,7 +47,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := RunLauncherRoboTests
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
 LOCAL_JAVA_LIBRARIES := LauncherRoboTests
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index 3d78689..0579400 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1 +1,17 @@
 sdk=29
+shadows= \
+    com.android.launcher3.shadows.LShadowAppPredictionManager \
+    com.android.launcher3.shadows.LShadowAppWidgetManager \
+    com.android.launcher3.shadows.LShadowBackupManager \
+    com.android.launcher3.shadows.LShadowBitmap \
+    com.android.launcher3.shadows.LShadowLauncherApps \
+    com.android.launcher3.shadows.LShadowTypeface \
+    com.android.launcher3.shadows.LShadowUserManager \
+    com.android.launcher3.shadows.LShadowWallpaperManager \
+    com.android.launcher3.shadows.ShadowDeviceFlag \
+    com.android.launcher3.shadows.ShadowLooperExecutor \
+    com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
+    com.android.launcher3.shadows.ShadowOverrides \
+    com.android.launcher3.shadows.ShadowSyncRtSurfaceTransactionApplierCompat \
+
+application=com.android.launcher3.util.LauncherTestApplication
\ No newline at end of file
diff --git a/robolectric_tests/resources/cache_data_updated_task_data.txt b/robolectric_tests/resources/cache_data_updated_task_data.txt
index 302d58f..603dbe3 100644
--- a/robolectric_tests/resources/cache_data_updated_task_data.txt
+++ b/robolectric_tests/resources/cache_data_updated_task_data.txt
@@ -1,6 +1,6 @@
 # Model data used by CacheDataUpdatedTaskTest
 
-classMap s com.android.launcher3.WorkspaceItemInfo
+classMap s com.android.launcher3.model.data.WorkspaceItemInfo
 
 # Items for the BgDataModel
 
diff --git a/robolectric_tests/resources/package_install_state_change_task_data.txt b/robolectric_tests/resources/package_install_state_change_task_data.txt
index 4d63664..e82ea9d 100644
--- a/robolectric_tests/resources/package_install_state_change_task_data.txt
+++ b/robolectric_tests/resources/package_install_state_change_task_data.txt
@@ -1,7 +1,7 @@
 # Model data used by PackageInstallStateChangeTaskTest
 
-classMap s com.android.launcher3.WorkspaceItemInfo
-classMap w com.android.launcher3.LauncherAppWidgetInfo
+classMap s com.android.launcher3.model.data.WorkspaceItemInfo
+classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
 
 # Items for the BgDataModel
 
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index 74ef55e..d927ffc 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -20,20 +20,20 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.os.UserHandle;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.shadows.LShadowUserManager;
-import com.android.launcher3.util.LauncherRoboTestRunner;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public final class FolderNameProviderTest {
     private Context mContext;
     private WorkspaceItemInfo mItem1;
@@ -45,13 +45,13 @@
         mItem1 = new WorkspaceItemInfo(new AppInfo(
                 new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
                 "title1",
-                LShadowUserManager.newUserHandle(10),
+                UserHandle.of(10),
                 new Intent().setComponent(new ComponentName("a.b.c", "a.b.c/a.b.c.d"))
         ));
         mItem2 = new WorkspaceItemInfo(new AppInfo(
                 new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
                 "title2",
-                LShadowUserManager.newUserHandle(10),
+                UserHandle.of(10),
                 new Intent().setComponent(new ComponentName("a.b.c", "a.b.c/a.b.c.d"))
         ));
     }
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index 95a4146..c892618 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -3,12 +3,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.launcher3.util.LauncherRoboTestRunner;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
 import org.robolectric.util.Scheduler;
@@ -21,7 +20,7 @@
 /**
  * Tests for {@link FileLog}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class FileLogTest {
 
     private File mTempDir;
diff --git a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index b7f2243..8aa6f37 100644
--- a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -14,23 +14,23 @@
 import android.util.Pair;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
@@ -41,7 +41,7 @@
 /**
  * Tests for {@link AddWorkspaceItemsTask}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class AddWorkspaceItemsTaskTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 7072adf..90313ab 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -43,11 +43,11 @@
 import com.android.launcher3.shadows.LShadowBackupManager;
 import com.android.launcher3.shadows.LShadowUserManager;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.shadow.api.Shadow;
@@ -55,7 +55,7 @@
 /**
  * Tests to verify backup and restore flow.
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(LooperMode.Mode.PAUSED)
 public class BackupRestoreTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index f128e24..5610b0e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -17,19 +17,19 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.cache.CachingLogic;
+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.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
@@ -40,7 +40,7 @@
 /**
  * Tests for {@link CacheDataUpdatedTask}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class CacheDataUpdatedTaskTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index 1442c55..b7340cf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -36,11 +36,11 @@
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.io.File;
@@ -48,7 +48,7 @@
 /**
  * Tests for {@link DbDowngradeHelper}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DbDowngradeHelperTest {
 
     private static final String SCHEMA_FILE = "test_schema.json";
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 7bc34cf..7ca416d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -28,17 +28,17 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
 
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
@@ -46,7 +46,7 @@
 /**
  * Tests for layout parser for remote layout
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class DefaultLayoutProviderTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
index f46b849..56ce215 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -23,17 +23,17 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Unit tests for {@link GridBackupTable}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class GridBackupTableTest {
 
     private static final int BACKUP_ITEM_COUNT = 12;
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index c426dc5..8e00dcb 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -20,11 +20,11 @@
 import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.HashSet;
@@ -33,7 +33,7 @@
 /**
  * Unit tests for {@link GridSizeMigrationTask}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class GridSizeMigrationTaskTest {
 
     private LauncherModelHelper mModelHelper;
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index 8f58d8b..59c9480 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -39,17 +39,17 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.HashSet;
 
 /** Unit tests for {@link GridSizeMigrationTaskV2} */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class GridSizeMigrationTaskV2Test {
 
     private LauncherModelHelper mModelHelper;
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 6e41a4f..2584f65 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -50,17 +50,17 @@
 import android.os.Process;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
@@ -68,7 +68,7 @@
 /**
  * Tests for {@link LoaderCursor}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class LoaderCursorTest {
 
@@ -127,7 +127,7 @@
                         new Intent().setComponent(cn), false  /* allowMissingTarget */, true))
                 .get();
         assertNotNull(info);
-        assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
+        assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent()));
     }
 
     @Test
@@ -141,7 +141,7 @@
                         new Intent().setComponent(cn), true  /* allowMissingTarget */, true))
                 .get();
         assertNotNull(info);
-        assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
+        assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent()));
     }
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index c7979b2..4b0ae7e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -26,20 +26,20 @@
 
 import android.os.Process;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
@@ -55,7 +55,7 @@
 /**
  * Tests to verify multiple callbacks in Loader
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class ModelMultiCallbacksTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index bd71f01..e43df21 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -2,16 +2,16 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
 
@@ -21,7 +21,7 @@
 /**
  * Tests for {@link PackageInstallStateChangedTask}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class PackageInstallStateChangedTaskTest {
 
diff --git a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 7612ae1..83bf7da 100644
--- a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -27,10 +27,9 @@
 
 import android.content.pm.ShortcutInfo;
 
-import com.android.launcher3.util.LauncherRoboTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
@@ -40,7 +39,7 @@
 /**
  * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class PopupPopulatorTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 7ef670c..58174c7 100644
--- a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -23,16 +23,16 @@
 
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for {@link RestoreDbTask}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class RestoreDbTaskTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
new file mode 100644
index 0000000..ae051f7
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowAppPredictionManager.java
@@ -0,0 +1,38 @@
+/*
+ * 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.shadows;
+
+import static org.mockito.Mockito.mock;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/**
+ * Shadow for {@link AppPredictionManager} which create mock predictors
+ */
+@Implements(value = AppPredictionManager.class)
+public class LShadowAppPredictionManager {
+
+    @Implementation
+    public AppPredictor createAppPredictionSession(AppPredictionContext predictionContext) {
+        return mock(AppPredictor.class);
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
index 576ddbd..edf8edb 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.shadows;
 
-import android.os.Parcel;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.SparseBooleanArray;
@@ -51,12 +50,4 @@
     public void setUserLocked(UserHandle userHandle, boolean enabled) {
         mLockedUsers.put(userHandle.hashCode(), enabled);
     }
-
-    // Create user handle from parcel since UserHandle.of() was only added in later APIs.
-    public static UserHandle newUserHandle(int uid) {
-        Parcel userParcel = Parcel.obtain();
-        userParcel.writeInt(uid);
-        userParcel.setDataPosition(0);
-        return new UserHandle(userParcel);
-    }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java
new file mode 100644
index 0000000..238926c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java
@@ -0,0 +1,42 @@
+/*
+ * 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.shadows;
+
+import static org.robolectric.shadow.api.Shadow.invokeConstructor;
+import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
+
+import android.view.View;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+
+/**
+ * Shadow for SyncRtSurfaceTransactionApplierCompat to override default functionality
+ */
+@Implements(className = "com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat",
+        isInAndroidSdk = false)
+public class ShadowSyncRtSurfaceTransactionApplierCompat {
+
+    @RealObject
+    private Object mRealObject;
+
+    @Implementation
+    protected void __constructor__(View view) {
+        invokeConstructor(mRealObject.getClass(), mRealObject, from(View.class, null));
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
index 209bae0..d330d10 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -14,23 +14,20 @@
  * limitations under the License.
  */package com.android.launcher3.ui;
 
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
 import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.LauncherUIHelper.buildAndBindLauncher;
+import static com.android.launcher3.util.LauncherUIHelper.doLayout;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Mockito.mock;
 
-import android.app.Activity;
 import android.content.Context;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerProperties;
-import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -44,24 +41,21 @@
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.util.LauncherRoboTestRunner;
-import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.widget.WidgetsFullSheet;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.annotation.LooperMode.Mode;
 import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.util.ReflectionHelpers;
 
 /**
  * Tests scroll behavior at various Launcher UI components
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
 public class LauncherUIScrollTest {
 
@@ -166,23 +160,7 @@
 
     private Launcher loadLauncher() throws Exception {
         mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
-
-        Launcher launcher = Robolectric.buildActivity(Launcher.class).setup().get();
-        doLayout(launcher);
-        ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
-        if (executor != null) {
-            executor.runAllTasks();
-        }
-        return launcher;
-    }
-
-    private static void doLayout(Activity activity) {
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE
-                .get(RuntimeEnvironment.application).portraitProfile;
-        View view = activity.getWindow().getDecorView();
-        view.measure(makeMeasureSpec(dp.widthPx, EXACTLY), makeMeasureSpec(dp.heightPx, EXACTLY));
-        view.layout(0, 0, dp.widthPx, dp.heightPx);
-        ShadowLooper.idleMainLooper();
+        return buildAndBindLauncher();
     }
 
     private static MotionEvent createScrollEvent(int scroll) {
diff --git a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
index e453e31..2f3fc37 100644
--- a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -1,16 +1,17 @@
 package com.android.launcher3.util;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
 /**
  * Unit tests for {@link GridOccupancy}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class GridOccupancyTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
index 5974ea5..c08e198 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
@@ -19,11 +19,12 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 /**
  * Robolectric unit tests for {@link IntArray}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class IntArrayTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
index aedf71e..7a8c00b 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
@@ -17,17 +17,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
 /**
  * Robolectric unit tests for {@link IntSet}
  */
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class IntSetTest {
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index d593d84..0388087 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -36,9 +36,7 @@
 import android.os.Process;
 import android.provider.Settings;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
@@ -47,6 +45,8 @@
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 
 import org.mockito.ArgumentCaptor;
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
deleted file mode 100644
index 744b478b..0000000
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
+++ /dev/null
@@ -1,101 +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.util;
-
-import static org.mockito.Mockito.mock;
-
-import com.android.launcher3.shadows.LShadowAppWidgetManager;
-import com.android.launcher3.shadows.LShadowBackupManager;
-import com.android.launcher3.shadows.LShadowBitmap;
-import com.android.launcher3.shadows.LShadowLauncherApps;
-import com.android.launcher3.shadows.LShadowTypeface;
-import com.android.launcher3.shadows.LShadowUserManager;
-import com.android.launcher3.shadows.LShadowWallpaperManager;
-import com.android.launcher3.shadows.ShadowDeviceFlag;
-import com.android.launcher3.shadows.ShadowLooperExecutor;
-import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
-import com.android.launcher3.shadows.ShadowOverrides;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.DefaultTestLifecycle;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.TestLifecycle;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLog;
-
-import java.lang.reflect.Method;
-
-import javax.annotation.Nonnull;
-
-/**
- * Test runner with Launcher specific configurations
- */
-public class LauncherRoboTestRunner extends RobolectricTestRunner {
-
-    private static final Class<?>[] SHADOWS = new Class<?>[] {
-            LShadowAppWidgetManager.class,
-            LShadowUserManager.class,
-            LShadowLauncherApps.class,
-            LShadowBitmap.class,
-            LShadowBackupManager.class,
-            LShadowTypeface.class,
-            LShadowWallpaperManager.class,
-            ShadowLooperExecutor.class,
-            ShadowMainThreadInitializedObject.class,
-            ShadowDeviceFlag.class,
-            ShadowOverrides.class
-    };
-
-    public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
-        super(testClass);
-    }
-
-    @Override
-    protected Config buildGlobalConfig() {
-        return new Config.Builder().setShadows(SHADOWS).build();
-    }
-
-    @Nonnull
-    @Override
-    protected Class<? extends TestLifecycle> getTestLifecycleClass() {
-        return LauncherTestLifecycle.class;
-    }
-
-    public static class LauncherTestLifecycle extends DefaultTestLifecycle {
-
-        @Override
-        public void beforeTest(Method method) {
-            super.beforeTest(method);
-            ShadowLog.stream = System.out;
-
-            // Disable plugins
-            PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
-
-            // Initialize mock wallpaper manager
-            LShadowWallpaperManager.initializeMock();
-        }
-
-        @Override
-        public void afterTest(Method method) {
-            super.afterTest(method);
-
-            ShadowLog.stream = null;
-            ShadowMainThreadInitializedObject.resetInitializedObjects();
-            ShadowOverrides.clearProvider();
-        }
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
new file mode 100644
index 0000000..6dd4df8
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static org.mockito.Mockito.mock;
+
+import android.app.Application;
+
+import com.android.launcher3.shadows.LShadowWallpaperManager;
+import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
+import com.android.launcher3.shadows.ShadowOverrides;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+
+import org.robolectric.TestLifecycleApplication;
+import org.robolectric.shadows.ShadowLog;
+
+import java.lang.reflect.Method;
+
+public class LauncherTestApplication extends Application implements TestLifecycleApplication {
+
+    @Override
+    public void beforeTest(Method method) {
+        ShadowLog.stream = System.out;
+
+        // Disable plugins
+        PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
+
+        // Initialize mock wallpaper manager
+        LShadowWallpaperManager.initializeMock();
+    }
+
+    @Override
+    public void prepareTest(Object test) { }
+
+    @Override
+    public void afterTest(Method method) {
+        ShadowLog.stream = null;
+        ShadowMainThreadInitializedObject.resetInitializedObjects();
+        ShadowOverrides.clearProvider();
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
new file mode 100644
index 0000000..f019a20
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
@@ -0,0 +1,102 @@
+/*
+ * 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.util;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Point;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.launcher3.Launcher;
+
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.List;
+
+/**
+ * Utility class to help manage Launcher UI and related objects for test.
+ */
+public class LauncherUIHelper {
+
+    /**
+     * Returns the class name for the Launcher activity as defined in the manifest
+     */
+    public static String getLauncherClassName() {
+        Context context = RuntimeEnvironment.application;
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(context.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        List<ResolveInfo> launchers = context.getPackageManager()
+                .queryIntentActivities(homeIntent, 0);
+        if (launchers.size() != 1) {
+            return null;
+        }
+        return launchers.get(0).activityInfo.name;
+    }
+
+    /**
+     * Returns an activity controller for Launcher activity defined in the manifest
+     */
+    public static <T extends Launcher> ActivityController<T> buildLauncher() {
+        try {
+            Class<T> tClass = (Class<T>) Class.forName(getLauncherClassName());
+            return Robolectric.buildActivity(tClass);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Creates and binds a Launcher activity defined in the manifest.
+     * Note that the model must be bound before calling this
+     */
+    public static <T extends Launcher> T buildAndBindLauncher() {
+        ActivityController<T> controller = buildLauncher();
+
+        T launcher = controller.setup().get();
+        doLayout(launcher);
+        ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
+        if (executor != null) {
+            executor.runAllTasks();
+        }
+        return launcher;
+    }
+
+    /**
+     * Performs a measure and layout pass for the given activity
+     */
+    public static void doLayout(Activity activity) {
+        Point size = new Point();
+        RuntimeEnvironment.application.getSystemService(WindowManager.class)
+                .getDefaultDisplay().getSize(size);
+        View view = activity.getWindow().getDecorView();
+        view.measure(makeMeasureSpec(size.x, EXACTLY), makeMeasureSpec(size.y, EXACTLY));
+        view.layout(0, 0, size.x, size.y);
+        ShadowLooper.idleMainLooper();
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index daae818..84c65b1 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -34,15 +34,15 @@
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.LauncherRoboTestRunner;
+import com.android.launcher3.model.data.PackageItemInfo;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.util.ReflectionHelpers;
@@ -50,7 +50,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 
-@RunWith(LauncherRoboTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class WidgetsListAdapterTest {
 
     @Mock private LayoutInflater mMockLayoutInflater;
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 73d8a88..6f7b684 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -350,7 +350,7 @@
         mWidgetView.requestLayout();
     }
 
-    static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
+    public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
             int spanX, int spanY) {
         getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
         widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 71b7206..a55c90d 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -17,6 +17,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ContentWriter;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index ac43967..5971a02 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -44,6 +44,8 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 6fa3c28..2dc7836 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -39,6 +39,8 @@
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 921e8ac..93247ab 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -53,7 +53,12 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconCache.IconLoadRequest;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconLabelDotView;
 
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 34d7067..09827d6 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 4259196..99416c4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -59,6 +59,7 @@
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ParcelableSparseArray;
@@ -959,8 +960,13 @@
                 height = r.height();
             }
 
-            if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
-                left +=  ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
+            // Center horizontaly
+            left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
+
+            if (v != null && v.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
+                // Center vertically
+                top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
+            } else if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
                 int cHeight = getShortcutsAndWidgets().getCellContentHeight();
                 int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
                 top += cellPaddingY;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 6f0ebd2..b4c5f96 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -28,6 +28,10 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.Snackbar;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4e1e586..fc3c9fd 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -25,8 +25,6 @@
 import android.util.DisplayMetrics;
 import android.view.Surface;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.icons.DotRenderer;
@@ -36,8 +34,6 @@
 public class DeviceProfile {
 
     public final InvariantDeviceProfile inv;
-    // IDP with no grid override values.
-    @Nullable private final InvariantDeviceProfile originalIdp;
 
     // Device properties
     public final boolean isTablet;
@@ -138,11 +134,10 @@
     public DotRenderer mDotRendererAllApps;
 
     public DeviceProfile(Context context, InvariantDeviceProfile inv,
-            InvariantDeviceProfile originalIDP, Point minSize, Point maxSize,
+            Point minSize, Point maxSize,
             int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
 
         this.inv = inv;
-        this.originalIdp = inv;
         this.isLandscape = isLandscape;
         this.isMultiWindowMode = isMultiWindowMode;
 
@@ -234,19 +229,6 @@
             // Recalculate the available dimensions using the new hotseat size.
             updateAvailableDimensions(dm, res);
         }
-
-        if (originalIDP != null) {
-            // Grid size change should not affect All Apps UI, so we use the original profile
-            // measurements here.
-            DeviceProfile originalProfile = isLandscape
-                    ? originalIDP.landscapeProfile
-                    : originalIDP.portraitProfile;
-            allAppsIconSizePx = originalProfile.iconSizePx;
-            allAppsIconTextSizePx = originalProfile.iconTextSizePx;
-            allAppsCellHeightPx = originalProfile.allAppsCellHeightPx;
-            allAppsIconDrawablePaddingPx = originalProfile.iconDrawablePaddingOriginalPx;
-            allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
-        }
         updateWorkspacePadding();
 
         // This is done last, after iconSizePx is calculated above.
@@ -259,8 +241,8 @@
 
     public DeviceProfile copy(Context context) {
         Point size = new Point(availableWidthPx, availableHeightPx);
-        return new DeviceProfile(context, inv, originalIdp, size, size, widthPx, heightPx,
-                isLandscape, isMultiWindowMode);
+        return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
+                isMultiWindowMode);
     }
 
     public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
@@ -271,8 +253,8 @@
         // In multi-window mode, we can have widthPx = availableWidthPx
         // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
         // widthPx and heightPx values where it's needed.
-        DeviceProfile profile = new DeviceProfile(context, inv, null, mwSize, mwSize,
-                mwSize.x, mwSize.y, isLandscape, true);
+        DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
+                isLandscape, true);
 
         // If there isn't enough vertical cell padding with the labels displayed, hide the labels.
         float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
@@ -356,11 +338,19 @@
         }
         cellWidthPx = iconSizePx + iconDrawablePaddingPx;
 
-        allAppsIconSizePx = iconSizePx;
-        allAppsIconTextSizePx = iconTextSizePx;
-        allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
-        allAppsCellHeightPx = getCellSize().y;
-        allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
+        // All apps
+        if (allAppsHasDifferentNumColumns()) {
+            allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, dm);
+            allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, dm);
+            allAppsCellHeightPx = getCellSize(inv.numAllAppsColumns, inv.numAllAppsColumns).y;
+            allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
+        } else {
+            allAppsIconSizePx = iconSizePx;
+            allAppsIconTextSizePx = iconTextSizePx;
+            allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
+            allAppsCellHeightPx = getCellSize().y;
+        }
+        allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
 
         if (isVerticalBarLayout()) {
             // Always hide the Workspace text with vertical bar layout.
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index ef02e87..0a0f9ad 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.FolderNameProvider;
+import com.android.launcher3.model.data.ItemInfo;
 
 /**
  * Interface defining an object that can receive a drag.
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index a78159f..f96aafa 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -35,6 +35,7 @@
 
 import com.android.launcher3.graphics.PlaceHolderIconDrawable;
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 
 
 public class FastBitmapDrawable extends Drawable {
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index f07040d..e5aecf7 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderPagedView;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.FocusLogic;
 import com.android.launcher3.util.Thunk;
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 78bd2ff..be941f2 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -28,6 +28,7 @@
 import android.widget.FrameLayout;
 
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index a5142d8..62c9b4d 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -46,6 +46,10 @@
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 7414a88..d2d0863 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -47,7 +47,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.launcher3.util.DefaultDisplay.Info;
@@ -61,7 +60,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 
 public class InvariantDeviceProfile {
 
@@ -107,6 +105,8 @@
     public int iconBitmapSize;
     public int fillResIconDpi;
     public float iconTextSize;
+    public float allAppsIconSize;
+    public float allAppsIconTextSize;
 
     private SparseArray<TypedValue> mExtraAttrs;
 
@@ -149,6 +149,8 @@
         numHotseatIcons = p.numHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
         dbFile = p.dbFile;
+        allAppsIconSize = p.allAppsIconSize;
+        allAppsIconTextSize = p.allAppsIconTextSize;
         defaultLayoutId = p.defaultLayoutId;
         demoModeLayoutId = p.demoModeLayoutId;
         mExtraAttrs = p.mExtraAttrs;
@@ -157,12 +159,9 @@
 
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
-        if (context instanceof LauncherPreviewRenderer.PreviewContext) {
-            throw new IllegalArgumentException(
-                    "PreviewContext is passed into this IDP constructor");
-        }
-
-        String gridName = getCurrentGridName(context);
+        String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
+                ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
+                : null;
         initGrid(context, gridName);
         mConfigMonitor = new ConfigMonitor(context,
                 APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
@@ -179,7 +178,7 @@
         }
     }
 
-    /**
+/**
      * This constructor should NOT have any monitors by design.
      */
     public InvariantDeviceProfile(Context context, Display display) {
@@ -211,11 +210,55 @@
         Point smallestSize = new Point(displayInfo.smallestSize);
         Point largestSize = new Point(displayInfo.largestSize);
 
+        ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
         // This guarantees that width < height
         float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
                 displayInfo.metrics);
         float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
                 displayInfo.metrics);
+        // Sort the profiles based on the closeness to the device size
+        Collections.sort(allOptions, (a, b) ->
+                Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
+                        dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
+        DisplayOption interpolatedDisplayOption =
+                invDistWeightedInterpolate(minWidthDps,  minHeightDps, allOptions);
+
+        GridOption closestProfile = allOptions.get(0).grid;
+        numRows = closestProfile.numRows;
+        numColumns = closestProfile.numColumns;
+        numHotseatIcons = closestProfile.numHotseatIcons;
+        dbFile = closestProfile.dbFile;
+        defaultLayoutId = closestProfile.defaultLayoutId;
+        demoModeLayoutId = closestProfile.demoModeLayoutId;
+        numFolderRows = closestProfile.numFolderRows;
+        numFolderColumns = closestProfile.numFolderColumns;
+        numAllAppsColumns = closestProfile.numAllAppsColumns;
+
+        mExtraAttrs = closestProfile.extraAttrs;
+
+        if (!closestProfile.name.equals(gridName)) {
+            Utilities.getPrefs(context).edit()
+                    .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
+        }
+
+        iconSize = interpolatedDisplayOption.iconSize;
+        iconShapePath = getIconShapePath(context);
+        landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
+        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
+        iconTextSize = interpolatedDisplayOption.iconTextSize;
+        fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
+
+        if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
+            allAppsIconSize = interpolatedDisplayOption.allAppsIconSize;
+            allAppsIconTextSize = interpolatedDisplayOption.allAppsIconTextSize;
+        } else {
+            allAppsIconSize = iconSize;
+            allAppsIconTextSize = iconTextSize;
+        }
+
+        // If the partner customization apk contains any grid overrides, apply them
+        // Supported overrides: numRows, numColumns, iconSize
+        applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
 
         Point realSize = new Point(displayInfo.realSize);
         // The real size never changes. smallSide and largeSide will remain the
@@ -223,64 +266,10 @@
         int smallSide = Math.min(realSize.x, realSize.y);
         int largeSide = Math.max(realSize.x, realSize.y);
 
-        // We want a list of all options as well as the list of filtered options. This allows us
-        // to have a consistent UI for areas that the grid size change should not affect
-        // ie. All Apps should be consistent between grid sizes.
-        ArrayList<DisplayOption> allOptions = new ArrayList<>();
-        ArrayList<DisplayOption> filteredOptions = new ArrayList<>();
-        getPredefinedDeviceProfiles(context, gridName, filteredOptions, allOptions);
-
-        if (allOptions.isEmpty() && filteredOptions.isEmpty()) {
-            throw new RuntimeException("No display option with canBeDefault=true");
-        }
-
-        // Sort the profiles based on the closeness to the device size
-        Comparator<DisplayOption> comparator = (a, b) -> Float.compare(dist(minWidthDps,
-                minHeightDps, a.minWidthDps, a.minHeightDps),
-                dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps));
-
-        // Calculate the device profiles as if there is no grid override.
-        Collections.sort(allOptions, comparator);
-        DisplayOption interpolatedDisplayOption =
-                invDistWeightedInterpolate(minWidthDps,  minHeightDps, allOptions);
-        initGridOption(context, allOptions, interpolatedDisplayOption, displayInfo.metrics);
-
-        // Create IDP with no grid override values.
-        InvariantDeviceProfile originalIDP = new InvariantDeviceProfile(this);
-        originalIDP.landscapeProfile = new DeviceProfile(context, this, null, smallestSize,
-                largestSize, largeSide, smallSide, true /* isLandscape */,
-                false /* isMultiWindowMode */);
-        originalIDP.portraitProfile = new DeviceProfile(context, this, null, smallestSize,
-                largestSize, smallSide, largeSide, false /* isLandscape */,
-                false /* isMultiWindowMode */);
-
-        if (filteredOptions.isEmpty()) {
-            filteredOptions = allOptions;
-
-            landscapeProfile = originalIDP.landscapeProfile;
-            portraitProfile = originalIDP.portraitProfile;
-        } else {
-            Collections.sort(filteredOptions, comparator);
-            interpolatedDisplayOption =
-                    invDistWeightedInterpolate(minWidthDps, minHeightDps, filteredOptions);
-
-            initGridOption(context, filteredOptions, interpolatedDisplayOption,
-                    displayInfo.metrics);
-            numAllAppsColumns = originalIDP.numAllAppsColumns;
-
-            landscapeProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
-                    largestSize, largeSide, smallSide, true /* isLandscape */,
-                    false /* isMultiWindowMode */);
-            portraitProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
-                    largestSize, smallSide, largeSide, false /* isLandscape */,
-                    false /* isMultiWindowMode */);
-        }
-
-        GridOption closestProfile = filteredOptions.get(0).grid;
-        if (!closestProfile.name.equals(gridName)) {
-            Utilities.getPrefs(context).edit()
-                    .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
-        }
+        landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+                largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
+        portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
+                smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
 
         // We need to ensure that there is enough extra space in the wallpaper
         // for the intended parallax effects
@@ -298,34 +287,6 @@
         return closestProfile.name;
     }
 
-    private void initGridOption(Context context, ArrayList<DisplayOption> options,
-            DisplayOption displayOption, DisplayMetrics metrics) {
-        GridOption closestProfile = options.get(0).grid;
-        numRows = closestProfile.numRows;
-        numColumns = closestProfile.numColumns;
-        numHotseatIcons = closestProfile.numHotseatIcons;
-        dbFile = closestProfile.dbFile;
-        defaultLayoutId = closestProfile.defaultLayoutId;
-        demoModeLayoutId = closestProfile.demoModeLayoutId;
-        numFolderRows = closestProfile.numFolderRows;
-        numFolderColumns = closestProfile.numFolderColumns;
-        numAllAppsColumns = numColumns;
-
-        mExtraAttrs = closestProfile.extraAttrs;
-
-        iconSize = displayOption.iconSize;
-        iconShapePath = getIconShapePath(context);
-        landscapeIconSize = displayOption.landscapeIconSize;
-        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
-        iconTextSize = displayOption.iconTextSize;
-        fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
-
-        // If the partner customization apk contains any grid overrides, apply them
-        // Supported overrides: numRows, numColumns, iconSize
-        applyPartnerDeviceProfileOverrides(context, metrics);
-    }
-
-
     @Nullable
     public TypedValue getAttrValue(int attr) {
         return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
@@ -403,13 +364,7 @@
         }
     }
 
-    /**
-     * @param gridName The current grid name.
-     * @param filteredOptionsOut List filled with all the filtered options based on gridName.
-     * @param allOptionsOut List filled with all the options that can be the default option.
-     */
-    static void getPredefinedDeviceProfiles(Context context, String gridName,
-            ArrayList<DisplayOption> filteredOptionsOut, ArrayList<DisplayOption> allOptionsOut) {
+    static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
@@ -436,19 +391,26 @@
             throw new RuntimeException(e);
         }
 
+        ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
         if (!TextUtils.isEmpty(gridName)) {
             for (DisplayOption option : profiles) {
                 if (gridName.equals(option.grid.name)) {
-                    filteredOptionsOut.add(option);
+                    filteredProfiles.add(option);
                 }
             }
         }
-
-        for (DisplayOption option : profiles) {
-            if (option.canBeDefault) {
-                allOptionsOut.add(option);
+        if (filteredProfiles.isEmpty()) {
+            // No grid found, use the default options
+            for (DisplayOption option : profiles) {
+                if (option.canBeDefault) {
+                    filteredProfiles.add(option);
+                }
             }
         }
+        if (filteredProfiles.isEmpty()) {
+            throw new RuntimeException("No display option with canBeDefault=true");
+        }
+        return filteredProfiles;
     }
 
     private int getLauncherIconDensity(int requiredSize) {
@@ -493,7 +455,7 @@
 
     @VisibleForTesting
     static DisplayOption invDistWeightedInterpolate(float width, float height,
-                ArrayList<DisplayOption> points) {
+            ArrayList<DisplayOption> points) {
         float weights = 0;
 
         DisplayOption p = points.get(0);
@@ -573,6 +535,8 @@
         private final int numHotseatIcons;
 
         private final String dbFile;
+        private final int numAllAppsColumns;
+
         private final int defaultLayoutId;
         private final int demoModeLayoutId;
 
@@ -596,6 +560,8 @@
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
             numFolderColumns = a.getInt(
                     R.styleable.GridDisplayOption_numFolderColumns, numColumns);
+            numAllAppsColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
 
             a.recycle();
 
@@ -615,6 +581,8 @@
         private float iconSize;
         private float iconTextSize;
         private float landscapeIconSize;
+        private float allAppsIconSize;
+        private float allAppsIconTextSize;
 
         DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
             this.grid = grid;
@@ -633,6 +601,10 @@
                     iconSize);
             iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
 
+            allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+                    iconSize);
+            allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+                    iconTextSize);
             a.recycle();
         }
 
@@ -647,14 +619,18 @@
         private DisplayOption multiply(float w) {
             iconSize *= w;
             landscapeIconSize *= w;
+            allAppsIconSize *= w;
             iconTextSize *= w;
+            allAppsIconTextSize *= w;
             return this;
         }
 
         private DisplayOption add(DisplayOption p) {
             iconSize += p.iconSize;
             landscapeIconSize += p.landscapeIconSize;
+            allAppsIconSize += p.allAppsIconSize;
             iconTextSize += p.iconTextSize;
+            allAppsIconTextSize += p.allAppsIconTextSize;
             return this;
         }
     }
@@ -672,4 +648,4 @@
             onConfigChanged(context);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 043ea2f..f69940b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -110,6 +110,12 @@
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ModelWriter;
+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;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.pm.UserCache;
@@ -133,6 +139,7 @@
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
@@ -294,6 +301,7 @@
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
     private SharedPreferences mSharedPrefs;
+    private OnboardingPrefs mOnboardingPrefs;
 
     // Activity result which needs to be processed after workspace has loaded.
     private ActivityResultInfo mPendingActivityResult;
@@ -361,6 +369,8 @@
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new LauncherStateManager(this);
 
+        mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs, mStateManager);
+
         mAppWidgetManager = new WidgetManagerHelper(this);
         mAppWidgetHost = new LauncherAppWidgetHost(this,
                 appWidgetId -> getWorkspace().removeWidget(appWidgetId));
@@ -452,6 +462,15 @@
         return new LauncherOverlayManager() { };
     }
 
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
+            LauncherStateManager stateManager) {
+        return new OnboardingPrefs<>(this, sharedPrefs, stateManager);
+    }
+
+    public OnboardingPrefs getOnboardingPrefs() {
+        return mOnboardingPrefs;
+    }
+
     @Override
     public void onPluginConnected(OverlayPlugin overlayManager, Context context) {
         switchOverlay(() -> overlayManager.createOverlayManager(this, this));
@@ -1443,6 +1462,8 @@
                 mLauncherCallbacks.onHomeIntent(internalStateHandled);
             }
             mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
+        } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
+            getStateManager().goToState(ALL_APPS, alreadyOnHome);
         }
 
         TraceHelper.INSTANCE.endSection(traceToken);
@@ -2557,9 +2578,10 @@
         writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
         writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
 
-        // Extra logging for b/116853349
+        // Extra logging for general debugging
         mDragLayer.dump(prefix, writer);
         mStateManager.dump(prefix, writer);
+        mPopupDataProvider.dump(prefix, writer);
 
         try {
             FileLog.flushAll(writer);
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 56cce78..618b5de 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -13,6 +13,7 @@
 
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 
 /**
  * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e61b7a8..3b1c7bb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -51,6 +51,9 @@
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
+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.pm.InstallSessionTracker;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
@@ -235,7 +238,8 @@
                 // we need to run the state change task again.
                 if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
                         Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
-                    enqueueModelUpdateTask(new UserLockStateChangedTask(user));
+                    enqueueModelUpdateTask(new UserLockStateChangedTask(
+                            user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
                 }
             }
         } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 5262b18..d3af5fc 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -22,6 +22,8 @@
 import android.os.Bundle;
 import android.provider.BaseColumns;
 
+import com.android.launcher3.model.data.ItemInfo;
+
 /**
  * Settings related utilities.
  */
@@ -151,7 +153,7 @@
         public static final int CONTAINER_PREDICTION = -102;
         public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
 
-        static final String containerToString(int container) {
+        public static final String containerToString(int container) {
             switch (container) {
                 case CONTAINER_DESKTOP: return "desktop";
                 case CONTAINER_HOTSEAT: return "hotseat";
@@ -160,7 +162,7 @@
             }
         }
 
-        static final String itemTypeToString(int type) {
+        public static final String itemTypeToString(int type) {
             switch(type) {
                 case ITEM_TYPE_APPLICATION: return "APP";
                 case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5e47e2f..5343424 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -38,7 +38,6 @@
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -55,12 +54,10 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.PagedViewOrientedState;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
@@ -121,8 +118,7 @@
     private float mLastMotion;
     private float mLastMotionRemainder;
     private float mTotalMotion;
-    protected PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler();
-    protected final PagedViewOrientedState mOrientationState = new PagedViewOrientedState();
+    protected PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT;
 
     protected int[] mPageScrolls;
     private boolean mIsBeingDragged;
@@ -144,9 +140,6 @@
 
     protected int mUnboundedScroll;
 
-    protected int mLayoutRotation = Surface.ROTATION_0;
-    protected int mDisplayRotation = Surface.ROTATION_0;
-
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     protected T mPageIndicator;
@@ -417,37 +410,6 @@
         return mUnboundedScroll;
     }
 
-    protected void updateLayoutRotation(int touchRotation) {
-        setLayoutRotation(touchRotation, mDisplayRotation);
-    }
-
-    /** @param touchRotation Must be one of {@link android.view.Surface.ROTATION_0/90/180/270} */
-    public void setLayoutRotation(int touchRotation, int displayRotation) {
-        if (mLayoutRotation == touchRotation && mDisplayRotation == displayRotation) {
-            return;
-        }
-
-        mOrientationState.update(touchRotation, displayRotation);
-        mOrientationHandler = mOrientationState.getOrientationHandler();
-        mLayoutRotation = touchRotation;
-        mDisplayRotation = displayRotation;
-        requestLayout();
-    }
-
-    public PagedViewOrientedState getPagedViewOrientedState() {
-        return mOrientationState;
-    }
-
-    public PagedOrientationHandler getPagedOrientationHandler() {
-        return getPagedViewOrientedState().getOrientationHandler();
-    }
-
-    public void disableMultipleLayoutRotations(boolean disable) {
-        mOrientationState.disableMultipleOrientations(disable);
-        mOrientationHandler = mOrientationState.getOrientationHandler();
-        requestLayout();
-    }
-
     @Override
     public void scrollBy(int x, int y) {
         mOrientationHandler.delegateScrollBy(this, getUnboundedScroll(), x, y);
@@ -955,7 +917,7 @@
                 // Remember location of down touch
                 mDownMotionX = x;
                 mDownMotionY = y;
-                mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
+                mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
                 mLastMotionRemainder = 0;
                 mTotalMotion = 0;
                 mActivePointerId = ev.getPointerId(0);
@@ -1107,16 +1069,28 @@
         }
     }
 
+    /**
+     * Returns the amount of overscroll caused by the spring in {@link OverScroller}.
+     */
+    private int getSpringOverScroll(int amount) {
+        if (mScroller.isSpringing()) {
+            return amount < 0
+                    ? mScroller.getCurrPos()
+                    : Math.max(0, mScroller.getCurrPos() - mMaxScroll);
+        } else {
+            return 0;
+        }
+    }
+
     protected void dampedOverScroll(int amount) {
-        mSpringOverScroll = amount;
         if (amount == 0) {
             return;
         }
 
         int size = mOrientationHandler.getMeasuredSize(this);
         int overScrollAmount = OverScroll.dampedScroll(amount, size);
-        mSpringOverScroll = overScrollAmount;
         if (mScroller.isSpringing()) {
+            mSpringOverScroll = getSpringOverScroll(amount);
             invalidate();
             return;
         }
@@ -1128,8 +1102,8 @@
     }
 
     protected void overScroll(int amount) {
-        mSpringOverScroll = amount;
         if (mScroller.isSpringing()) {
+            mSpringOverScroll = getSpringOverScroll(amount);
             invalidate();
             return;
         }
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 76de3e7..29c9d93 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -18,6 +18,8 @@
 
 import android.content.ComponentName;
 
+import com.android.launcher3.model.data.ItemInfo;
+
 /**
  * Meta data that is used for deferred binding.
  * e.g., this object is used to pass information on draggable targets when they are dropped onto
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 983c289..9dbb5fc 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -3,12 +3,12 @@
 import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
 import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
@@ -35,6 +35,9 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.PackageManagerHelper;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0cd08d4..3c3ab6c 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
 
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -66,6 +66,8 @@
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7c504a6..70a5cc5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -77,6 +77,11 @@
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.logging.UserEventDispatcher;
+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;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.states.StateAnimationConfig;
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 0b9d602..c3d4aeb 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -21,6 +21,7 @@
 
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.touch.ItemLongClickListener;
 
 public interface WorkspaceLayoutManager {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 414abab..136d43e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -19,26 +19,26 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.keyboard.CustomActionsPopup;
+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;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.touch.ItemLongClickListener;
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index c0c0b37..d4ba11e 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -23,11 +23,11 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationMainView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 17daeb8..65a261d 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -21,17 +21,17 @@
 import android.text.TextUtils;
 import android.view.View;
 
-import com.android.launcher3.AppInfo;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.dragndrop.DragLayer;
-
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+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.WorkspaceItemInfo;
 
 /**
  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 8d1a102..a45c96c 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -42,7 +42,6 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
@@ -50,10 +49,11 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -645,8 +645,8 @@
             if (!mIsWork || recyclerView == null) return;
             boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
             if (mWorkDisabled == workDisabled) return;
-            recyclerView.setContentDescription(
-                    workDisabled ? mLauncher.getString(R.string.work_apps_paused_title) : null);
+            recyclerView.setContentDescription(workDisabled ? mLauncher.getString(
+                    R.string.work_apps_paused_content_description) : null);
             View overlayView = getOverlayView();
             recyclerView.setItemAnimator(new DefaultItemAnimator());
             if (workDisabled) {
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 1f861bc..4aebec0 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -37,12 +37,12 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.List;
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 8fe4633..069472f 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -35,11 +35,11 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.RecyclerViewFastScroller;
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index a6ef10a..d2dcfd2 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -15,17 +15,17 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
-import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
 
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index b501c82..06209bb 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LabelComparator;
diff --git a/src/com/android/launcher3/allapps/AppInfoComparator.java b/src/com/android/launcher3/allapps/AppInfoComparator.java
index 8baf56c..823f98e 100644
--- a/src/com/android/launcher3/allapps/AppInfoComparator.java
+++ b/src/com/android/launcher3/allapps/AppInfoComparator.java
@@ -19,7 +19,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 
-import com.android.launcher3.AppInfo;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.LabelComparator;
 
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0f0fc3a..fc29a30 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -24,7 +24,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
-import android.content.SharedPreferences;
 import android.os.Handler;
 import android.os.UserManager;
 import android.view.MotionEvent;
@@ -35,6 +34,7 @@
 import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.OnboardingPrefs;
 
 /**
  * Abstract base class of floating view responsible for showing discovery bounce animation
@@ -43,13 +43,6 @@
 
     private static final long DELAY_MS = 450;
 
-    public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
-    public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
-    public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
-    public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
-
-    public static final int BOUNCE_MAX_COUNT = 3;
-
     private final Launcher mLauncher;
     private final Animator mDiscoBounceAnimation;
 
@@ -142,8 +135,9 @@
     }
 
     private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
+        OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
         if (!launcher.isInState(NORMAL)
-                || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
+                || onboardingPrefs.getBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN)
                 || AbstractFloatingView.getTopOpenView(launcher) != null
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -154,7 +148,7 @@
             new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
             return;
         }
-        incrementHomeBounceCount(launcher);
+        onboardingPrefs.incrementEventCount(OnboardingPrefs.HOME_BOUNCE_COUNT);
 
         new DiscoveryBounce(launcher, 0).show(HOTSEAT);
     }
@@ -164,11 +158,12 @@
     }
 
     private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
+        OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
         if (!launcher.isInState(OVERVIEW)
                 || !launcher.hasBeenResumed()
                 || launcher.isForceInvisible()
                 || launcher.getDeviceProfile().isVerticalBarLayout()
-                || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
+                || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             return;
@@ -182,7 +177,7 @@
             // TODO: Move these checks to the top and call this method after invalidate handler.
             return;
         }
-        incrementShelfBounceCount(launcher);
+        onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT);
 
         new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
                 .show(PREDICTION);
@@ -209,22 +204,4 @@
             mController.setProgress(progress - mDelta);
         }
     }
-
-    private static void incrementShelfBounceCount(Launcher launcher) {
-        SharedPreferences sharedPrefs = launcher.getSharedPrefs();
-        int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
-        if (count > BOUNCE_MAX_COUNT) {
-            return;
-        }
-        sharedPrefs.edit().putInt(SHELF_BOUNCE_COUNT, count + 1).apply();
-    }
-
-    private static void incrementHomeBounceCount(Launcher launcher) {
-        SharedPreferences sharedPrefs = launcher.getSharedPrefs();
-        int count = sharedPrefs.getInt(HOME_BOUNCE_COUNT, 0);
-        if (count > BOUNCE_MAX_COUNT) {
-            return;
-        }
-        sharedPrefs.edit().putInt(HOME_BOUNCE_COUNT, count + 1).apply();
-    }
 }
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 2515c24..2de425e 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -135,8 +135,8 @@
     @Override
     public void setActiveMarker(int activePage) {
         updateTabTextColor(activePage);
-        updateIndicatorPosition(activePage);
         if (mContainerView != null && mLastActivePage != activePage) {
+            updateIndicatorPosition(activePage);
             mContainerView.onTabChanged(activePage);
         }
         mLastActivePage = activePage;
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 05db18e..33262b6 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -25,6 +25,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 import android.widget.Switch;
 
 import com.android.launcher3.Insettable;
@@ -46,16 +48,28 @@
 
     private Rect mInsets = new Rect();
 
+    private final float[] mTouch = new float[2];
+    private int mTouchSlop;
+
     public WorkModeSwitch(Context context) {
         super(context);
+        init();
     }
 
     public WorkModeSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        init();
+
     }
 
     public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
+        mTouchSlop = viewConfiguration.getScaledTouchSlop();
     }
 
     @Override
@@ -73,7 +87,7 @@
 
     private void setCheckedInternal(boolean checked) {
         super.setChecked(checked);
-        setCompoundDrawablesWithIntrinsicBounds(
+        setCompoundDrawablesRelativeWithIntrinsicBounds(
                 checked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
     }
 
@@ -84,6 +98,25 @@
         setEnabled(true);
     }
 
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mTouch[0] = ev.getX();
+            mTouch[1] = ev.getY();
+        } else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
+            if (Math.abs(mTouch[0] - ev.getX()) > mTouchSlop
+                    || Math.abs(mTouch[1] - ev.getY()) > mTouchSlop) {
+                int action = ev.getAction();
+                ev.setAction(MotionEvent.ACTION_CANCEL);
+                super.onTouchEvent(ev);
+                ev.setAction(action);
+                return false;
+            }
+        }
+        return super.onTouchEvent(ev);
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 26f6ec3..f72a988 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -17,7 +17,7 @@
 
 import android.os.Handler;
 
-import com.android.launcher3.AppInfo;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.ComponentKey;
 
 import java.text.Collator;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ec34350..bcd91da 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -92,8 +92,8 @@
     public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
             "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
 
-    public static final BooleanFlag ENABLE_HINTS_IN_OVERVIEW = getDebugFlag(
-            "ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen");
+    public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
+            "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
 
     public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
             "FOLDER_NAME_SUGGEST", true,
diff --git a/src/com/android/launcher3/dot/DotInfo.java b/src/com/android/launcher3/dot/DotInfo.java
index 4ff0539..fc180d1 100644
--- a/src/com/android/launcher3/dot/DotInfo.java
+++ b/src/com/android/launcher3/dot/DotInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.dot;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 
@@ -83,4 +85,10 @@
     public int getNotificationCount() {
         return Math.min(mTotalCount, MAX_COUNT);
     }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return Integer.toString(mTotalCount);
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index db61f59..d93fb1a 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -37,11 +37,11 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.TouchController;
 
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7c76d34..d07635b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -46,7 +46,6 @@
 
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.FirstFrameAnimatorHelper;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
@@ -55,6 +54,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 77c6306..bf3aa7f 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -30,10 +30,10 @@
 import android.widget.RemoteViews;
 
 import com.android.launcher3.DragSource;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index f71cfb8..9982b39 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -35,8 +35,8 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 
diff --git a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
index 9f617e4..c501ab5 100644
--- a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
+++ b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
@@ -25,8 +25,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
 /**
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 202836d..b9b33fe 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -20,7 +20,6 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
-import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
@@ -28,6 +27,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.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
@@ -65,16 +65,12 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Alarm;
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
@@ -84,7 +80,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.config.FeatureFlags;
@@ -92,12 +87,18 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.FolderInfo.FolderListener;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.userevent.LauncherLogProto.Action;
 import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.LauncherLogProto.ItemType;
 import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
 import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Thunk;
@@ -110,6 +111,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.OptionalInt;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
@@ -346,7 +348,7 @@
         }
 
         mInfo.title = newTitle;
-        mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, getAcceptedSuggestionIndex() < 0,
+        mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !getAcceptedSuggestionIndex().isPresent(),
                 mLauncher.getModelWriter());
         mFolderIcon.onTitleChanged(newTitle);
         mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -1679,36 +1681,58 @@
     private Target.ToFolderLabelState getToFolderLabelState() {
         String newLabel =
                 checkNotNull(mFolderName.getText().toString(),
-                        "Expected valid folder label, but found null");
+                "Expected valid folder label, but found null");
+        if (newLabel.equals(mPreviousLabel)) {
+            return Target.ToFolderLabelState.UNCHANGED;
+        }
+
+        if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
+            return newLabel.isEmpty()
+                ? ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED
+                : ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
+        }
 
         Optional<String[]> suggestedLabels = getSuggestedLabels();
-        int accepted_suggestion_index = getAcceptedSuggestionIndex();
-        boolean hasValidPrimary = suggestedLabels
-                .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
-                .orElse(false);
-        String primarySuffix = hasValidPrimary
-                ? "_WITH_VALID_PRIMARY"
-                : "_WITH_EMPTY_PRIMARY";
-
         boolean isEmptySuggestions = suggestedLabels
                 .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
                 .orElse(true);
-        boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
-        String suggestionsSuffix =  isSuggestionsEnabled
-                ? isEmptySuggestions
-                    ? "_WITH_EMPTY_SUGGESTIONS"
-                    : "_WITH_VALID_SUGGESTIONS"
-                : "_WITH_SUGGESTIONS_DISABLED";
+        if (isEmptySuggestions) {
+            return newLabel.isEmpty()
+                ? ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS
+                : ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
+        }
 
-        return newLabel.equals(mPreviousLabel)
-                ? Target.ToFolderLabelState.UNCHANGED
-                : newLabel.isEmpty()
-                    ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
-                    : accepted_suggestion_index >= 0
-                        ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
-                            + accepted_suggestion_index
-                            + primarySuffix)
-                        : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
+        boolean hasValidPrimary = suggestedLabels
+                .map(labels -> !isEmpty(labels[0]))
+                .orElse(false);
+        if (newLabel.isEmpty()) {
+            return hasValidPrimary ? ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY
+                : ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+        }
+
+        OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
+        if (!accepted_suggestion_index.isPresent()) {
+            return hasValidPrimary ? ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY
+                : ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+        }
+
+        switch (accepted_suggestion_index.getAsInt()) {
+            case 0:
+                return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
+            case 1:
+                return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY
+                    : ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+            case 2:
+                return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY
+                    : ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+            case 3:
+                return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY
+                    : ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+            default:
+                // fall through
+        }
+        return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
+
     }
 
     private Optional<String[]> getSuggestedLabels() {
@@ -1728,19 +1752,18 @@
                         .toArray(String[]::new));
     }
 
-    private int getAcceptedSuggestionIndex() {
-        String newLabel =
-                checkNotNull(mFolderName.getText().toString(),
-                        "Expected valid folder label, but found null");
-
+    private OptionalInt getAcceptedSuggestionIndex() {
+        String newLabel = checkNotNull(mFolderName.getText().toString(),
+                "Expected valid folder label, but found null");
         return getSuggestedLabels()
                 .map(suggestionsArray ->
-                        IntStream.range(0, suggestionsArray.length)
-                                .filter(index -> newLabel.equalsIgnoreCase(
-                                        suggestionsArray[index]))
-                                .findFirst()
-                                .orElse(-1)
-                ).orElse(-1);
+                    IntStream.range(0, suggestionsArray.length)
+                        .filter(
+                            index -> !isEmpty(suggestionsArray[index])
+                                && newLabel.equalsIgnoreCase(suggestionsArray[index]))
+                        .sequential()
+                        .findFirst()
+                ).orElse(OptionalInt.empty());
 
     }
 
@@ -1761,7 +1784,6 @@
 
     private Target.Builder newParentContainerTarget() {
         Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
-
         switch (mInfo.container) {
             case CONTAINER_HOTSEAT:
                 return builder.setContainerType(ContainerType.HOTSEAT);
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
index 9d14a5f..4be82ed 100644
--- a/src/com/android/launcher3/folder/FolderGridOrganizer.java
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -20,9 +20,9 @@
 
 import android.graphics.Point;
 
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index eda9545..680c3ba 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -38,22 +38,17 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.Alarm;
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.FolderDotInfo;
@@ -62,6 +57,11 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.FolderInfo.FolderListener;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Thunk;
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 07161da..7731e6e 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -22,14 +22,14 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ResourceBasedOverride;
 
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index dcd0e14..32531c0 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.PagedView;
@@ -44,9 +43,10 @@
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Thunk;
@@ -153,6 +153,7 @@
             CellLayout page = (CellLayout) getChildAt(i);
             ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets();
             for (int j = container.getChildCount() - 1; j >= 0; j--) {
+                container.getChildAt(j).setVisibility(View.VISIBLE);
                 mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j));
             }
             page.removeAllViews();
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index caf6e55..a14a0d8 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -17,7 +17,7 @@
 
 import android.graphics.drawable.Drawable;
 
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 /**
  * Manages the parameters used to draw a Folder preview item.
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 27aa43e..7f8a15c 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -38,8 +38,8 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index ed9dfbb..848c04a 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -92,6 +92,8 @@
     public Bitmap createDragBitmap() {
         int width = 0;
         int height = 0;
+        // Assume scaleX == scaleY, which is always the case for workspace items.
+        float scale = mView.getScaleX();
         if (mView instanceof DraggableView) {
             ((DraggableView) mView).getVisualDragBounds(mTempRect);
             width = mTempRect.width();
@@ -102,7 +104,7 @@
         }
 
         return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
-                height + blurSizeOutline, (c) -> drawDragView(c, 1));
+                height + blurSizeOutline, (c) -> drawDragView(c, scale));
     }
 
     public final void generateDragOutline(Bitmap preview) {
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java
index 607aba9..af974f8 100644
--- a/src/com/android/launcher3/graphics/GridOptionsProvider.java
+++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java
@@ -20,7 +20,6 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
 import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.PreviewSurfaceRenderer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -201,12 +200,11 @@
     }
 
     @Override
-    public Bundle call(String method, String arg, Bundle extras)  {
+    public Bundle call(String method, String arg, Bundle extras) {
         if (!METHOD_GET_PREVIEW.equals(method)) {
             return null;
         }
 
-        PreviewSurfaceRenderer.render(getContext(), extras);
-        return null;
+        return new PreviewSurfaceRenderer(getContext(), extras).render();
     }
 }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 7d4eb0e..6e91d70 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -54,19 +54,15 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.config.FeatureFlags;
@@ -84,6 +80,10 @@
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index b0e1db1..4439284 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -35,8 +35,8 @@
 import android.util.SparseArray;
 
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 
 import java.lang.ref.WeakReference;
 
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
new file mode 100644
index 0000000..20eec9a
--- /dev/null
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.graphics;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.Display;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+
+import com.android.launcher3.InvariantDeviceProfile;
+
+import java.util.concurrent.TimeUnit;
+
+/** Render preview using surface view. */
+public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
+
+    private static final String KEY_HOST_TOKEN = "host_token";
+    private static final String KEY_VIEW_WIDTH = "width";
+    private static final String KEY_VIEW_HEIGHT = "height";
+    private static final String KEY_DISPLAY_ID = "display_id";
+    private static final String KEY_SURFACE_PACKAGE = "surface_package";
+    private static final String KEY_CALLBACK = "callback";
+
+    private final Context mContext;
+    private final InvariantDeviceProfile mIdp;
+    private final IBinder mHostToken;
+    private final int mWidth;
+    private final int mHeight;
+    private final Display mDisplay;
+
+    private SurfaceControlViewHost mSurfaceControlViewHost;
+
+    PreviewSurfaceRenderer(Context context, Bundle bundle) {
+        mContext = context;
+
+        String gridName = bundle.getString("name");
+        bundle.remove("name");
+        if (gridName == null) {
+            gridName = InvariantDeviceProfile.getCurrentGridName(context);
+        }
+        mIdp = new InvariantDeviceProfile(context, gridName);
+
+        mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
+        mWidth = bundle.getInt(KEY_VIEW_WIDTH);
+        mHeight = bundle.getInt(KEY_VIEW_HEIGHT);
+
+        final DisplayManager displayManager = (DisplayManager) context.getSystemService(
+                Context.DISPLAY_SERVICE);
+        mDisplay = displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID));
+    }
+
+    /** Handle a received surface view request. */
+    Bundle render() {
+        if (mSurfaceControlViewHost != null) {
+            binderDied();
+        }
+
+        try {
+            mSurfaceControlViewHost = MAIN_EXECUTOR
+                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
+                    .get(5, TimeUnit.SECONDS);
+            mHostToken.linkToDeath(this, 0);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        MAIN_EXECUTOR.execute(() -> {
+            View view = new LauncherPreviewRenderer(mContext, mIdp).getRenderedView();
+            // This aspect scales the view to fit in the surface and centers it
+            final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
+                    mHeight / (float) view.getMeasuredHeight());
+            view.setScaleX(scale);
+            view.setScaleY(scale);
+            view.setPivotX(0);
+            view.setPivotY(0);
+            view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
+            view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
+            mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
+                    view.getMeasuredHeight());
+        });
+
+        Bundle result = new Bundle();
+        result.putParcelable(KEY_SURFACE_PACKAGE, mSurfaceControlViewHost.getSurfacePackage());
+
+        Handler handler = new Handler(Looper.getMainLooper(), Loopermessage -> {
+            binderDied();
+            return true;
+        });
+        Messenger messenger = new Messenger(handler);
+        Message msg = Message.obtain();
+        msg.replyTo = messenger;
+        result.putParcelable(KEY_CALLBACK, msg);
+        return result;
+    }
+
+    @Override
+    public void binderDied() {
+        if (mSurfaceControlViewHost != null) {
+            mSurfaceControlViewHost.release();
+            mSurfaceControlViewHost = null;
+        }
+        mHostToken.unlinkToDeath(this, 0);
+    }
+}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index f27c9da..ff0f773 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -38,18 +38,18 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
 import com.android.launcher3.icons.cache.BaseIconCache;
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.icons.cache.HandlerRunnable;
-import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 938955c..800598e 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -24,9 +24,9 @@
 import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 1b70fde..0f79bd6 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -19,10 +19,10 @@
 import android.util.SparseArray;
 import android.view.View;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 8449612..97aaf84 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -7,7 +7,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 89077ee..da081a0 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -46,11 +46,11 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index eb95395..c236fa6 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -23,16 +23,16 @@
 import android.util.LongSparseArray;
 import android.util.Pair;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+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;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 9f1843f..4f349ca 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -16,8 +16,8 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
-import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,10 +33,10 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.AppFilter;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 0d12183..c98be56 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -21,14 +21,14 @@
 
 import android.util.Log;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.LooperIdleLock;
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 5a7b4d3..7ce970d 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,13 +17,13 @@
 
 import android.util.Log;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.widget.WidgetListRowEntry;
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 206688a..f79a9d1 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -26,16 +26,16 @@
 import android.util.Log;
 import android.util.MutableInt;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
+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;
+import com.android.launcher3.model.data.PromiseAppInfo;
+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;
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index c1c8be3..8e6b064 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,11 +18,11 @@
 import android.content.ComponentName;
 import android.os.UserHandle;
 
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.util.ArrayList;
 import java.util.HashSet;
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index a0b7177..5112304 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -15,16 +15,18 @@
  */
 package com.android.launcher3.model;
 
+import static android.os.Process.myUserHandle;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.util.Log;
 
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -35,8 +37,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.os.Process.myUserHandle;
-
 /**
  * Helper class to send broadcasts to package installers that have:
  * - Items on the first screen
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index a084600..b27e4ea 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -22,7 +22,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
@@ -32,6 +31,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 79ae4c5..1c44fc3 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -36,12 +36,12 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.GridOccupancy;
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 695d2a6..244de96 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -37,19 +37,19 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
+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.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fc0997b..4c02837 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,11 +16,11 @@
 
 package com.android.launcher3.model;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
 import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
@@ -45,16 +45,11 @@
 import android.util.MutableInt;
 import android.util.TimingLogger;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
@@ -67,6 +62,12 @@
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
+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;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
@@ -534,7 +535,7 @@
                                             pinnedShortcut.getPackage(), info.user)) {
                                         info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
                                     }
-                                    intent = info.intent;
+                                    intent = info.getIntent();
                                     allDeepShortcuts.add(pinnedShortcut);
                                 } else {
                                     // Create a shortcut info in disabled mode for now.
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 1473124..ef7e828 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -16,9 +16,9 @@
 package com.android.launcher3.model;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.IntSet;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 27fa580..2c99df7 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -27,20 +27,20 @@
 import android.os.Looper;
 import android.util.Log;
 
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.ItemInfoMatcher;
 
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 2832150..203f1ca 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -19,13 +19,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.PromiseAppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 48c56e9..0d97596 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-import static com.android.launcher3.WorkspaceItemInfo.FLAG_RESTORED_ICON;
+import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,18 +29,18 @@
 import android.util.Log;
 
 import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.FlagOp;
@@ -233,7 +233,7 @@
                                 } else if (!isTargetValid) {
                                     removedShortcuts.put(si.id, true);
                                     FileLog.e(TAG, "Restored shortcut no longer valid "
-                                            + si.intent);
+                                            + si.getIntent());
                                     continue;
                                 } else {
                                     si.status = WorkspaceItemInfo.DEFAULT;
diff --git a/src/com/android/launcher3/model/PagedViewOrientedState.java b/src/com/android/launcher3/model/PagedViewOrientedState.java
deleted file mode 100644
index e48b8c1..0000000
--- a/src/com/android/launcher3/model/PagedViewOrientedState.java
+++ /dev/null
@@ -1,106 +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 android.view.Surface;
-
-import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
-import com.android.launcher3.touch.LandscapePagedViewHandler;
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.SeascapePagedViewHandler;
-
-/**
- * Container to hold orientation/rotation related information for Launcher.
- * This is not meant to be an abstraction layer for applying different functionality between
- * the different orientation/rotations. For that see {@link PagedOrientationHandler}
- *
- * This class has initial default state assuming the device and foreground app have
- * no ({@link Surface.ROTATION_0} rotation.
- *
- * Currently this class resides in {@link com.android.launcher3.PagedView}, but there's a ticket
- * to disassociate it from Launcher since it's needed before Launcher is instantiated
- * See TODO(b/150300347)
- */
-public final class PagedViewOrientedState {
-
-    private PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler();
-
-    private int mTouchRotation = Surface.ROTATION_0;
-    private int mDisplayRotation = Surface.ROTATION_0;
-    /**
-     * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake
-     * launcher orientations.
-     */
-    private boolean mDisableMultipleOrientations;
-
-    /**
-     * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
-     * @param touchRotation The rotation the nav bar region that is touched is in
-     * @param displayRotation Rotation of the display/device
-     */
-    public void update(int touchRotation, int displayRotation) {
-        if (mDisableMultipleOrientations) {
-            return;
-        }
-
-        mDisplayRotation = displayRotation;
-        mTouchRotation = touchRotation;
-        if (mTouchRotation == Surface.ROTATION_90) {
-            mOrientationHandler = new LandscapePagedViewHandler();
-        } else if (mTouchRotation == Surface.ROTATION_270) {
-            mOrientationHandler = new SeascapePagedViewHandler();
-        } else {
-            mOrientationHandler = new PortraitPagedViewHandler();
-        }
-    }
-
-    public boolean areMultipleLayoutOrientationsDisabled() {
-        return mDisableMultipleOrientations;
-    }
-
-    /**
-     * Setting this preference will render future calls to {@link #update(int, int)} as a no-op.
-     */
-    public void disableMultipleOrientations(boolean disable) {
-        mDisableMultipleOrientations = disable;
-        if (disable) {
-            mOrientationHandler = new PortraitPagedViewHandler();
-        }
-    }
-
-    public int getDisplayRotation() {
-        return mDisplayRotation;
-    }
-
-    /**
-     * Gets the difference between the rotation of the device/display and which region the
-     * user is currently interacting with in factors of 90 degree clockwise rotations.
-     * Ex. Display is in portrait -> 0, user touches landscape region -> 1, this
-     * method would return 3 because it takes 3 clockwise 90 degree rotations from normal to
-     * landscape (portrait -> seascape -> reverse portrait -> landscape)
-     */
-    public int getTouchDisplayDelta() {
-        return RotationHelper.deltaRotation(mTouchRotation, mDisplayRotation);
-    }
-
-    public PagedOrientationHandler getOrientationHandler() {
-        return mOrientationHandler;
-    }
-}
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 3f79ad0..1cbe5c2 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,10 +19,10 @@
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.ItemInfoMatcher;
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index a3adc82..7ec884f 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -15,17 +15,16 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
-import android.os.UserManager;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
@@ -43,18 +42,19 @@
 public class UserLockStateChangedTask extends BaseModelUpdateTask {
 
     private final UserHandle mUser;
+    private boolean mIsUserUnlocked;
 
-    public UserLockStateChangedTask(UserHandle user) {
+    public UserLockStateChangedTask(UserHandle user, boolean isUserUnlocked) {
         mUser = user;
+        mIsUserUnlocked = isUserUnlocked;
     }
 
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         Context context = app.getContext();
-        boolean isUserUnlocked = context.getSystemService(UserManager.class).isUserUnlocked(mUser);
 
         HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
-        if (isUserUnlocked) {
+        if (mIsUserUnlocked) {
             QueryResult shortcuts = new ShortcutRequest(context, mUser)
                     .query(ShortcutRequest.PINNED);
             if (shortcuts.wasSuccess()) {
@@ -65,7 +65,7 @@
                 // Shortcut manager can fail due to some race condition when the lock state
                 // changes too frequently. For the purpose of the update,
                 // consider it as still locked.
-                isUserUnlocked = false;
+                mIsUserUnlocked = false;
             }
         }
 
@@ -77,7 +77,7 @@
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                     && mUser.equals(itemInfo.user)) {
                 WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
-                if (isUserUnlocked) {
+                if (mIsUserUnlocked) {
                     ShortcutKey key = ShortcutKey.fromItemInfo(si);
                     ShortcutInfo shortcut = pinnedShortcuts.get(key);
                     // We couldn't verify the shortcut during loader. If its no longer available
@@ -108,7 +108,7 @@
             }
         }
 
-        if (isUserUnlocked) {
+        if (mIsUserUnlocked) {
             dataModel.updateDeepShortcutCounts(
                     null, mUser,
                     new ShortcutRequest(context, mUser).query(ShortcutRequest.ALL));
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
similarity index 94%
rename from src/com/android/launcher3/AppInfo.java
rename to src/com/android/launcher3/model/data/AppInfo.java
index f76ca50..ae79f0d 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,6 +28,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -121,7 +123,8 @@
         return new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setComponent(cn)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
     public static void updateRuntimeFlagsForActivityTarget(
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
similarity index 96%
rename from src/com/android/launcher3/FolderInfo.java
rename to src/com/android/launcher3/model/data/FolderInfo.java
index b75a5e7..cfe34c1 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import android.content.Intent;
 import android.os.Process;
 
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
 
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
similarity index 92%
rename from src/com/android/launcher3/ItemInfo.java
rename to src/com/android/launcher3/model/data/ItemInfo.java
index 6f5b941..a1c3111 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
@@ -31,11 +31,12 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.util.ContentWriter;
 
-
-
 /**
  * Represents an item in the launcher.
  */
@@ -50,18 +51,18 @@
     public int id = NO_ID;
 
     /**
-     * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET} or
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
+     * One of {@link Favorites#ITEM_TYPE_APPLICATION},
+     * {@link Favorites#ITEM_TYPE_SHORTCUT},
+     * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+     * {@link Favorites#ITEM_TYPE_FOLDER},
+     * {@link Favorites#ITEM_TYPE_APPWIDGET} or
+     * {@link Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
      */
     public int itemType;
 
     /**
      * The id of the container that holds this item. For the desktop, this will be
-     * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. For the all applications folder it
+     * {@link Favorites#CONTAINER_DESKTOP}. For the all applications folder it
      * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
      * it will be the id of the folder.
      */
@@ -69,8 +70,8 @@
 
     /**
      * Indicates the screen in which the shortcut appears if the container types is
-     * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
-     * {@link LauncherSettings.Favorites#CONTAINER_HOTSEAT})
+     * {@link Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
+     * {@link Favorites#CONTAINER_HOTSEAT})
      */
     public int screenId = -1;
 
@@ -125,7 +126,7 @@
         user = Process.myUserHandle();
     }
 
-    ItemInfo(ItemInfo info) {
+    protected ItemInfo(ItemInfo info) {
         copyFrom(info);
     }
 
@@ -200,7 +201,7 @@
     protected String dumpProperties() {
         return "id=" + id
                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
-                + " container=" + LauncherSettings.Favorites.containerToString((int)container)
+                + " container=" + LauncherSettings.Favorites.containerToString(container)
                 + " targetComponent=" + getTargetComponent()
                 + " screen=" + screenId
                 + " cell(" + cellX + "," + cellY + ")"
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
similarity index 93%
rename from src/com/android/launcher3/ItemInfoWithIcon.java
rename to src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 1941455..d95f94f 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import com.android.launcher3.icons.BitmapInfo;
 
@@ -60,9 +60,9 @@
      */
     public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5;
 
-    public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE |
-            FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED |
-            FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER;
+    public static final int FLAG_DISABLED_MASK = FLAG_DISABLED_SAFEMODE
+            | FLAG_DISABLED_NOT_AVAILABLE | FLAG_DISABLED_SUSPENDED
+            | FLAG_DISABLED_QUIET_USER | FLAG_DISABLED_BY_PUBLISHER | FLAG_DISABLED_LOCKED_USER;
 
     /**
      * The item points to a system app.
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
similarity index 93%
rename from src/com/android/launcher3/LauncherAppWidgetInfo.java
rename to src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 3a478dd..adb97dc 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Process;
 
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ContentWriter;
 
 /**
@@ -154,7 +156,7 @@
      * When we bind the widget, we should notify the widget that the size has changed if we have not
      * done so already (only really for default workspace widgets).
      */
-    void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
+    public void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
         if (!mHasNotifiedInitialWidgetSizeChanged) {
             AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
             mHasNotifiedInitialWidgetSizeChanged = true;
@@ -169,8 +171,8 @@
     }
 
     public final boolean isWidgetIdAllocated() {
-        return (restoreStatus & FLAG_ID_NOT_VALID) == 0 ||
-                (restoreStatus & FLAG_ID_ALLOCATED) == FLAG_ID_ALLOCATED;
+        return (restoreStatus & FLAG_ID_NOT_VALID) == 0
+                || (restoreStatus & FLAG_ID_ALLOCATED) == FLAG_ID_ALLOCATED;
     }
 
     public final boolean hasRestoreFlag(int flag) {
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/data/PackageItemInfo.java
similarity index 95%
rename from src/com/android/launcher3/model/PackageItemInfo.java
rename to src/com/android/launcher3/model/data/PackageItemInfo.java
index 2fc064c..b70d0d4 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/data/PackageItemInfo.java
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.model;
+package com.android.launcher3.model.data;
 
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherSettings;
 
 import java.util.Objects;
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/model/data/PromiseAppInfo.java
similarity index 97%
rename from src/com/android/launcher3/PromiseAppInfo.java
rename to src/com/android/launcher3/model/data/PromiseAppInfo.java
index e55e4bd..b6231ed 100644
--- a/src/com/android/launcher3/PromiseAppInfo.java
+++ b/src/com/android/launcher3/model/data/PromiseAppInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import android.content.Context;
 import android.content.Intent;
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
similarity index 94%
rename from src/com/android/launcher3/WorkspaceItemInfo.java
rename to src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index be907e5..a7bf1f3 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model.data;
 
 import android.app.Person;
 import android.content.ComponentName;
@@ -25,7 +25,9 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.uioverrides.ApiWrapper;
@@ -120,7 +122,7 @@
     public WorkspaceItemInfo(AppInfo info) {
         super(info);
         title = Utilities.trim(info.title);
-        intent = new Intent(info.intent);
+        intent = new Intent(info.getIntent());
     }
 
     /**
@@ -199,8 +201,8 @@
 
     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
     public String getDeepShortcutId() {
-        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
-                getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
+        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                ? getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
     }
 
     @NonNull
@@ -211,8 +213,8 @@
     @Override
     public ComponentName getTargetComponent() {
         ComponentName cn = super.getTargetComponent();
-        if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
-                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI|FLAG_AUTOINSTALL_ICON|FLAG_RESTORED_ICON))) {
+        if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
+                FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
             // Legacy shortcuts and promise icons with web UI may not have a componentName but just
             // a packageName. In that case create a dummy componentName instead of adding additional
             // check everywhere.
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index b193ffd..b03aa9c 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -34,10 +34,10 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index 74a5a31..179061f 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -30,8 +30,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 public class PinRequestHelper {
 
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index ac0e065..40d7031 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -40,9 +40,9 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 406e1b2..331298f 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -49,10 +49,9 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
 import com.android.launcher3.dot.DotInfo;
@@ -60,6 +59,8 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.notification.NotificationKeyData;
@@ -397,6 +398,9 @@
         } else if (view instanceof ImageView) {
             // Only the system shortcut icon shows on a gray background header.
             info.setIconAndContentDescriptionFor((ImageView) view);
+            if (Utilities.ATLEAST_OREO) {
+                view.setTooltipText(view.getContentDescription());
+            }
         }
         view.setTag(info);
         view.setOnClickListener(info);
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 1092c7b..5a5f668 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -23,9 +23,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ComponentKey;
@@ -33,6 +33,7 @@
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -238,6 +239,11 @@
                 }).collect(Collectors.toList());
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "PopupDataProvider:");
+        writer.println(prefix + "\tmPackageUserToDotInfos:" + mPackageUserToDotInfos);
+    }
+
     public interface PopupDataChangeListener {
 
         PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index fdcf04f..7da86cc 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -25,10 +25,10 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 8751202..3875d2e 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -33,9 +33,9 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 @TargetApi(Build.VERSION_CODES.Q)
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 21c5ac5..ae35d4c 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -14,11 +14,11 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.InstantAppResolver;
@@ -145,8 +145,9 @@
                 && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
                         WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
         boolean isInstantApp = false;
-        if (itemInfo instanceof com.android.launcher3.AppInfo) {
-            com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
+        if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
+            com.android.launcher3.model.data.AppInfo
+                    appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
             isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
         }
         boolean enabled = supportsWebUI || isInstantApp;
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 732fb0b..a5462a6 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -37,7 +37,6 @@
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.DefaultLayoutParser;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -46,6 +45,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 0fe3673..33eff57 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -35,13 +35,13 @@
 import com.android.launcher3.AppWidgetsRestoredReceiver;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridBackupTable;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.LogConfig;
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index 54b7fb9..e9058c3 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -30,12 +30,12 @@
 import android.widget.BaseAdapter;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AppInfoComparator;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.ComponentKey;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 1cc01f4..f1f271f 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -28,18 +28,18 @@
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 936d377..e35e884 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -30,9 +30,9 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.TouchController;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 9cc7d8f..e9b92e2 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 
 /**
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index fa1a85f..3ca9490 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -6,7 +6,7 @@
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ComponentKey;
 
 /**
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 8bb6a08..2e0521f 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -31,12 +31,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.provider.Settings;
-import android.view.MotionEvent;
-import android.view.Surface;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -258,138 +253,6 @@
         }
     }
 
-    public static int getDegreesFromRotation(int rotation) {
-        int degrees;
-        switch (rotation) {
-            case Surface.ROTATION_90:
-                degrees = 90;
-                break;
-            case Surface.ROTATION_180:
-                degrees = 180;
-                break;
-            case Surface.ROTATION_270:
-                degrees = 270;
-                break;
-            case Surface.ROTATION_0:
-            default:
-                degrees = 0;
-                break;
-        }
-        return degrees;
-    }
-
-    public static int getRotationFromDegrees(float degrees) {
-        int threshold = 70;
-        if (degrees >= (360 - threshold) || degrees < (threshold)) {
-            return Surface.ROTATION_0;
-        } else if (degrees < (90 + threshold)) {
-            return Surface.ROTATION_270;
-        } else if (degrees < 180 + threshold) {
-            return Surface.ROTATION_180;
-        } else {
-            return Surface.ROTATION_90;
-        }
-    }
-
-    /**
-     * @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
-     * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
-     * A value of 0 means no rotation has been applied
-     */
-    public static int deltaRotation(int oldRotation, int newRotation) {
-        int delta = newRotation - oldRotation;
-        if (delta < 0) delta += 4;
-        return delta;
-    }
-
-    /**
-     * For landscape, since the navbar is already in a vertical position, we don't have to do any
-     * rotations as the change in Y coordinate is what is read. We only flip the sign of the
-     * y coordinate to make it match existing behavior of swipe to the top to go previous
-     */
-    public static void transformEventForNavBar(MotionEvent ev, boolean inverse) {
-        // TODO(b/151269990): Use a temp matrix
-        Matrix m = new Matrix();
-        m.setScale(1, -1);
-        if (inverse) {
-            Matrix inv = new Matrix();
-            m.invert(inv);
-            ev.transform(inv);
-        } else {
-            ev.transform(m);
-        }
-    }
-
-    /**
-     * Creates a matrix to transform the given motion event specified by degrees.
-     * If {@param inverse} is {@code true}, the inverse of that matrix will be applied
-     */
-    public static void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
-        Matrix transform = new Matrix();
-        // TODO(b/151269990): Use a temp matrix
-        transform.setRotate(degrees);
-        if (inverse) {
-            Matrix inv = new Matrix();
-            transform.invert(inv);
-            ev.transform(inv);
-        } else {
-            ev.transform(transform);
-        }
-        // TODO: Add scaling back in based on degrees
-//        if (getWidth() > 0 && getHeight() > 0) {
-//            float scale = ((float) getWidth()) / getHeight();
-//            transform.postScale(scale, 1 / scale);
-//        }
-    }
-
-    /**
-     * TODO(b/149658423): Have {@link com.android.quickstep.OrientationTouchTransformer
-     *   also use this}
-     */
-    public static Matrix getRotationMatrix(int screenWidth, int screenHeight, int displayRotation) {
-        Matrix m = new Matrix();
-        // TODO(b/151269990): Use a temp matrix
-        switch (displayRotation) {
-            case Surface.ROTATION_0:
-                return m;
-            case Surface.ROTATION_90:
-                m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation));
-                m.postTranslate(0, screenWidth);
-                break;
-            case Surface.ROTATION_270:
-                m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation));
-                m.postTranslate(screenHeight, 0);
-                break;
-        }
-        return m;
-    }
-
-    public static void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight,
-        int displayRotation) {
-        Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
-        m.mapRect(src);
-    }
-
-    public static void mapInverseRectFromNormalOrientation(RectF src, int screenWidth,
-        int screenHeight, int displayRotation) {
-        Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
-        Matrix inverse = new Matrix();
-        m.invert(inverse);
-        inverse.mapRect(src);
-    }
-
-    public static void getTargetRectForRotation(Rect srcOut, int screenWidth, int screenHeight,
-        int displayRotation) {
-        RectF wrapped = new RectF(srcOut);
-        Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation);
-        m.mapRect(wrapped);
-        wrapped.round(srcOut);
-    }
-
-    public static boolean isRotationLandscape(int rotation) {
-        return rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90;
-    }
-
     @Override
     public String toString() {
         return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 3181752..015de59 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -100,5 +100,4 @@
     public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
     public static final String APP_NOT_DISABLED = "b/139891609";
     public static final String NO_SCROLL_END_WIDGETS = "b/152354290";
-    public static final String NO_START_FROM_RECENTS = "b/152658211";
 }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f7ce160..f7091fc 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -15,14 +15,14 @@
  */
 package com.android.launcher3.touch;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
 import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 
 import android.app.AlertDialog;
 import android.content.Intent;
@@ -38,19 +38,19 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+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;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.views.FloatingIconView;
@@ -224,8 +224,9 @@
 
         // Check for abandoned promise
         if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
-            String packageName = shortcut.intent.getComponent() != null ?
-                    shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
+            String packageName = shortcut.getIntent().getComponent() != null
+                    ? shortcut.getIntent().getComponent().getPackageName()
+                    : shortcut.getIntent().getPackage();
             if (!TextUtils.isEmpty(packageName)) {
                 onClickPendingAppItem(v, launcher, packageName,
                         shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 8537bdf..7baeab8 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -28,11 +28,11 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 6715bc1..bab5747 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -65,11 +65,11 @@
     }
 
     @Override
-    public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) {
+    public void getCurveProperties(PagedView view, Rect insets, CurveProperties out) {
         out.scroll = view.getScrollY();
         out.halfPageSize = view.getNormalChildHeight() / 2;
         out.halfScreenSize = view.getMeasuredHeight() / 2;
-        out.screenCenter = mInsets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
+        out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
     }
 
     @Override
@@ -157,11 +157,6 @@
     }
 
     @Override
-    public float getViewCenterPosition(View view) {
-        return view.getTop() + view.getTranslationY();
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollY();
     }
@@ -205,6 +200,11 @@
     }
 
     @Override
+    public float getChildStartWithTranslation(View view) {
+        return view.getTop() + view.getTranslationY();
+    }
+
+    @Override
     public int getCenterForPage(View view, Rect insets) {
         return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left
             - insets.right - view.getPaddingRight()) / 2;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 24fa815..50606ec 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -39,6 +39,10 @@
  */
 public interface PagedOrientationHandler {
 
+    PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
+    PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
+    PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
+
     interface Int2DAction<T> {
         void call(T target, int x, int y);
     }
@@ -61,10 +65,10 @@
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
     void setPrimaryAndResetSecondaryTranslate(View view, float translation);
-    float getViewCenterPosition(View view);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
+    float getChildStartWithTranslation(View view);
     int getCenterForPage(View view, Rect insets);
     int getScrollOffsetStart(View view, Rect insets);
     int getScrollOffsetEnd(View view, Rect insets);
@@ -83,7 +87,7 @@
     void delegateScrollTo(PagedView pagedView, int primaryScroll);
     void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
     void scrollerStartScroll(OverScroller scroller, int newPosition);
-    void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out);
+    void getCurveProperties(PagedView view, Rect insets, CurveProperties out);
     boolean isGoingUp(float displacement);
 
     /**
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 6d903b3..245138f 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -65,11 +65,11 @@
     }
 
     @Override
-    public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) {
+    public void getCurveProperties(PagedView view, Rect insets, CurveProperties out) {
         out.scroll = view.getScrollX();
         out.halfPageSize = view.getNormalChildWidth() / 2;
         out.halfScreenSize = view.getMeasuredWidth() / 2;
-        out.screenCenter = mInsets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
+        out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
     }
 
     @Override
@@ -155,11 +155,6 @@
     }
 
     @Override
-    public float getViewCenterPosition(View view) {
-        return view.getLeft() + view.getTranslationX();
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollX();
     }
@@ -203,6 +198,11 @@
     }
 
     @Override
+    public float getChildStartWithTranslation(View view) {
+        return view.getLeft() + view.getTranslationX();
+    }
+
+    @Override
     public int getCenterForPage(View view, Rect insets) {
         return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top
             - insets.bottom - view.getPaddingBottom()) / 2;
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
index 6a10b0a..9c752a7 100644
--- a/src/com/android/launcher3/util/GridOccupancy.java
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -2,7 +2,7 @@
 
 import android.graphics.Rect;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 
 /**
  * Utility object to manage the occupancy in a grid.
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 031a40d..6f706d2 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -21,8 +21,8 @@
 import android.content.pm.PackageManager;
 import android.util.Log;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.model.data.AppInfo;
 
 /**
  * A wrapper class to access instant app related APIs.
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 59a5ed6..4d5405d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -19,11 +19,11 @@
 import android.content.ComponentName;
 import android.os.UserHandle;
 
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.HashSet;
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
new file mode 100644
index 0000000..baa1eee
--- /dev/null
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import android.content.SharedPreferences;
+import android.util.ArrayMap;
+
+import androidx.annotation.StringDef;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherStateManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Stores and retrieves onboarding-related data via SharedPreferences.
+ */
+public class OnboardingPrefs<T extends Launcher> {
+
+    public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
+    public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
+    public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
+    public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
+    public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
+
+    /**
+     * Events that either have happened or have not (booleans).
+     */
+    @StringDef(value = {
+            HOME_BOUNCE_SEEN,
+            SHELF_BOUNCE_SEEN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventBoolKey {}
+
+    /**
+     * Events that occur multiple times, which we count up to a max defined in {@link #MAX_COUNTS}.
+     */
+    @StringDef(value = {
+            HOME_BOUNCE_COUNT,
+            SHELF_BOUNCE_COUNT,
+            ALL_APPS_COUNT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventCountKey {}
+
+    private static final Map<String, Integer> MAX_COUNTS;
+    static {
+        Map<String, Integer> maxCounts = new ArrayMap<>(3);
+        maxCounts.put(HOME_BOUNCE_COUNT, 3);
+        maxCounts.put(SHELF_BOUNCE_COUNT, 3);
+        maxCounts.put(ALL_APPS_COUNT, 5);
+        MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
+    }
+
+    protected final T mLauncher;
+    protected final SharedPreferences mSharedPrefs;
+    protected final LauncherStateManager mStateManager;
+
+    public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs,
+            LauncherStateManager stateManager) {
+        mLauncher = launcher;
+        mSharedPrefs = sharedPrefs;
+        mStateManager = stateManager;
+    }
+
+    /** @return The number of times we have seen the given event. */
+    public int getCount(@EventCountKey String key) {
+        return mSharedPrefs.getInt(key, 0);
+    }
+
+    /** @return Whether we have seen this event enough times, as defined by {@link #MAX_COUNTS}. */
+    public boolean hasReachedMaxCount(@EventCountKey String eventKey) {
+        return hasReachedMaxCount(getCount(eventKey), eventKey);
+    }
+
+    private boolean hasReachedMaxCount(int count, @EventCountKey String eventKey) {
+        return count >= MAX_COUNTS.get(eventKey);
+    }
+
+    /** @return Whether we have seen the given event. */
+    public boolean getBoolean(@EventBoolKey String key) {
+        return mSharedPrefs.getBoolean(key, false);
+    }
+
+    /**
+     * Add 1 to the given event count, if we haven't already reached the max count.
+     * @return Whether we have now reached the max count.
+     */
+    public boolean incrementEventCount(@EventCountKey String eventKey) {
+        int count = getCount(eventKey);
+        if (hasReachedMaxCount(count, eventKey)) {
+            return true;
+        }
+        count++;
+        mSharedPrefs.edit().putInt(eventKey, count).apply();
+        return hasReachedMaxCount(count, eventKey);
+    }
+}
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
index 34efb12..87e6986 100644
--- a/src/com/android/launcher3/util/OverScroller.java
+++ b/src/com/android/launcher3/util/OverScroller.java
@@ -561,10 +561,11 @@
             mStartTime = AnimationUtils.currentAnimationTimeMillis();
             mDuration = duration;
 
+            if (mSpring != null) {
+                mSpring.cancel();
+            }
+
             if (mState == SPRING) {
-                if (mSpring != null) {
-                    mSpring.cancel();
-                }
                 mSpring = new SpringAnimation(this, SPRING_PROPERTY);
 
                 ResourceProvider rp = DynamicResource.provider(mContext);
@@ -576,9 +577,9 @@
                 mSpring.setStartVelocity(velocity);
                 mSpring.animateToFinalPosition(mFinal);
                 mSpring.addEndListener((animation, canceled, value, velocity1) -> {
+                    mSpring = null;
                     finish();
                     mState = SPLINE;
-                    mSpring = null;
                 });
             }
             // Unused
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 6c18747..ddde6d3 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -47,14 +47,14 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.net.URISyntaxException;
 import java.util.List;
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index f243ca6..3a3b5a2 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -3,9 +3,10 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 
 import java.util.Arrays;
 
@@ -60,4 +61,10 @@
         PackageUserKey otherKey = (PackageUserKey) obj;
         return mPackageName.equals(otherKey.mPackageName) && mUser.equals(otherKey.mUser);
     }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return mPackageName + "#" + mUser;
+    }
 }
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
index b8bcfed..9b8c6a6 100644
--- a/src/com/android/launcher3/util/PendingRequestArgs.java
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -20,7 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
 /**
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 49c97da..1ec0690 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,11 +15,11 @@
  */
 package com.android.launcher3.util;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 public class ShortcutUtil {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index c9cdeff..ae459e1 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -21,8 +21,8 @@
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
 
 /**
  * An interface to be used along with a context for various activities in Launcher. This allows a
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index ad8d69d..e114cf8 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -50,7 +50,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -58,6 +57,7 @@
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 39e1eac..442c5fd 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -82,7 +82,7 @@
  * Simple scrim which draws a flat color
  */
 public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
-        AccessibilityStateChangeListener, StateListener {
+        AccessibilityStateChangeListener {
 
     public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
             new IntProperty<ScrimView>("dragHandleAlpha") {
@@ -116,6 +116,18 @@
     private final AccessibilityManager mAM;
     protected final int mEndScrim;
 
+    private final StateListener mAccessibilityLauncherStateListener = new StateListener() {
+        @Override
+        public void onStateTransitionStart(LauncherState toState) {}
+
+        @Override
+        public void onStateTransitionComplete(LauncherState finalState) {
+            setImportantForAccessibility(finalState == ALL_APPS
+                    ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                    : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        }
+    };
+
     protected float mMaxScrimAlpha;
 
     protected float mProgress = 1;
@@ -177,7 +189,7 @@
     @Override
     public void setInsets(Rect insets) {
         updateDragHandleBounds();
-        updateDragHandleVisibility(null);
+        updateDragHandleVisibility();
     }
 
     @Override
@@ -375,18 +387,22 @@
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         LauncherStateManager stateManager = mLauncher.getStateManager();
-        stateManager.removeStateListener(this);
+        stateManager.removeStateListener(mAccessibilityLauncherStateListener);
 
         if (enabled) {
-            stateManager.addStateListener(this);
-            handleStateChangedComplete(stateManager.getState());
+            stateManager.addStateListener(mAccessibilityLauncherStateListener);
+            mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState());
         } else {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
         }
+        updateDragHandleVisibility();
+    }
+
+    public void updateDragHandleVisibility() {
         updateDragHandleVisibility(null);
     }
 
-    private void updateDragHandleVisibility(Drawable recycle) {
+    private void updateDragHandleVisibility(@Nullable Drawable recycle) {
         boolean visible = shouldDragHandleBeVisible();
         boolean wasVisible = mDragHandle != null;
         if (visible != wasVisible) {
@@ -424,20 +440,6 @@
         mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
     }
 
-    @Override
-    public void onStateTransitionStart(LauncherState toState) {}
-
-    @Override
-    public void onStateTransitionComplete(LauncherState finalState) {
-        handleStateChangedComplete(finalState);
-    }
-
-    private void handleStateChangedComplete(LauncherState finalState) {
-        setImportantForAccessibility(finalState == ALL_APPS
-                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-    }
-
     protected class AccessibilityHelper extends ExploreByTouchHelper {
 
         private static final int DRAG_HANDLE_ID = 1;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 23c2160..3e5113a 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -28,10 +28,10 @@
 
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 78acc34..5d33f13 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -35,14 +35,14 @@
 import android.widget.RemoteViews;
 
 import com.android.launcher3.CheckLongPressHelper;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
@@ -365,10 +365,9 @@
 
     @Override
     public void getVisualDragBounds(Rect bounds) {
-        int x = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
-        int y = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
-        int width = (int) getScaleToFit() * getMeasuredWidth();
-        int height = (int) getScaleToFit() * getMeasuredHeight();
-        bounds.set(x, y , x + width, y + height);
+        int width = (int) (getMeasuredWidth() * mScaleToFit);
+        int height = (int) (getMeasuredHeight() * mScaleToFit);
+
+        bounds.set(0, 0 , width, height);
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 895f8de..9021d9e 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -37,12 +37,12 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Themes;
 
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index 5387be8..ebc2a25 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -20,10 +20,10 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.PendingRequestArgs;
 
 /**
diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java
index 335b8c7..17e4673 100644
--- a/src/com/android/launcher3/widget/WidgetListRowEntry.java
+++ b/src/com/android/launcher3/widget/WidgetListRowEntry.java
@@ -15,9 +15,9 @@
  */
 package com.android.launcher3.widget;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
 
 import java.util.ArrayList;
 
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index f3c7822..4b6c569 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -28,10 +28,10 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 8d1a3b0..30be7a6 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -32,12 +32,12 @@
 import android.widget.TextView;
 
 import com.android.launcher3.Insettable;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.List;
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index f3b325d..df6e2c3 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -21,7 +21,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index b3e9734..37a30af 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -193,6 +193,7 @@
                     .setDuration(DEFAULT_OPEN_DURATION)
                     .setInterpolator(AnimationUtils.loadInterpolator(
                             getContext(), android.R.interpolator.linear_out_slow_in));
+            mRecyclerView.setLayoutFrozen(true);
             mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -202,7 +203,6 @@
                 }
             });
             post(() -> {
-                mRecyclerView.setLayoutFrozen(true);
                 mOpenCloseAnimator.start();
                 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
             });
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 7ec6214..63e063f 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.R;
@@ -220,4 +221,13 @@
         }
         super.setLayoutFrozen(frozen);
     }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
+                    "onInitializeAccessibilityNodeInfo, scrollable: " + info.isScrollable());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 0ea7d85..0b66ec0 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -29,8 +29,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageUserKey;
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 0b99e7a..9d87788 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index a56801f..313ea05 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index 293b04a..bdf01f3 100644
--- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -20,14 +20,14 @@
 
 import android.content.ComponentName;
 
-import com.android.launcher3.AppInfo;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.model.data.AppInfo;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 /**
  * Unit tests for {@link DefaultAppSearchAlgorithm}
  */
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 873f1cb..a7089fe 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -47,7 +47,6 @@
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
@@ -55,6 +54,7 @@
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.tapl.TestHelpers;
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index d93915c..0a6579a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -28,10 +28,10 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 788e041..5e26aa6 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -23,8 +23,8 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Widget;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 001a88f..62ce085 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -40,10 +40,10 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.tapl.Widget;
 import com.android.launcher3.tapl.Workspace;
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0246f95..0e43d81 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -29,12 +29,12 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.tapl.AddToHomeScreenPrompt;
 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 975fe9c..2968c80 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -247,6 +247,8 @@
                 }
             }
         }
+
+        disableSensorRotation();
     }
 
     public void enableCheckEventsForSuccessfulGestures() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 49901ea..78dfc36 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -44,6 +44,7 @@
 
     private static final String START_PREFIX = "START_READER ";
     private static final String FINISH_PREFIX = "FINISH_READER ";
+    private static final String SKIP_EVENTS_TAG = "b/153670015";
 
     private volatile CountDownLatch mFinished;
 
@@ -72,6 +73,7 @@
             mFinished = null;
         }
         mEvents.clear();
+        Log.d(SKIP_EVENTS_TAG, "Cleared events");
         mExpectedEvents.clear();
         mEventsCounter.drainPermits();
         final String id = UUID.randomUUID().toString();
@@ -89,27 +91,30 @@
 
             try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                     Runtime.getRuntime().exec(cmd).getInputStream()))) {
-                for (;;) {
+                for (; ; ) {
                     // Skip everything before the next start command.
-                    for (;;) {
+                    for (; ; ) {
                         final String event = reader.readLine();
                         if (event.contains(TestProtocol.TAPL_EVENTS_TAG)
                                 && event.contains(mStartCommand)) {
+                            Log.d(SKIP_EVENTS_TAG, "Read start: " + event);
                             break;
                         }
                     }
 
                     // Store all actual events until the finish command.
-                    for (;;) {
+                    for (; ; ) {
                         final String event = reader.readLine();
                         if (event.contains(TestProtocol.TAPL_EVENTS_TAG)) {
                             if (event.contains(mFinishCommand)) {
                                 mFinished.countDown();
+                                Log.d(SKIP_EVENTS_TAG, "Read finish: " + event);
                                 break;
                             } else {
                                 final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
                                 if (matcher.find()) {
                                     mEvents.add(matcher.group("sequence"), matcher.group("event"));
+                                    Log.d(SKIP_EVENTS_TAG, "Read event: " + event);
                                     mEventsCounter.release();
                                 }
                             }
@@ -154,6 +159,7 @@
             String sequence = expectedEvents.getKey();
 
             List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
+            Log.d(SKIP_EVENTS_TAG, "Verifying events");
             final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
             hasMismatches = hasMismatches || mismatchPosition != -1;
             formatSequenceWithMismatch(
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 5be57c6..39ac645 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+
 import android.graphics.Point;
 import android.graphics.Rect;
 
@@ -23,6 +25,7 @@
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
 
@@ -88,9 +91,12 @@
     }
 
     public Widget getWidget(String labelText) {
-        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                "getting widget " + labelText + " in widgets list")) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "getting widget " + labelText + " in widgets list")) {
             final UiObject2 widgetsContainer = verifyActiveContainer();
+            mLauncher.assertTrue("Widgets container didn't become scrollable",
+                    widgetsContainer.wait(Until.scrollable(true), WAIT_TIME_MS));
             final Point displaySize = mLauncher.getRealDisplaySize();
             final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
 
@@ -114,17 +120,17 @@
                         maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth);
                     }
 
-                    int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width();
-                    if (visibleDelta > 0) {
-                        Rect parentBounds = mLauncher.getVisibleBounds(cell);
-                        mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
-                                        + mLauncher.getTouchSlop(),
-                                parentBounds.centerY(), parentBounds.centerX(),
-                                parentBounds.centerY(), 10, true, GestureScope.INSIDE);
-                    }
-
                     if (mLauncher.getVisibleBounds(widget).bottom
                             <= displaySize.y - mLauncher.getBottomGestureSize()) {
+                        int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width();
+                        if (visibleDelta > 0) {
+                            Rect parentBounds = mLauncher.getVisibleBounds(cell);
+                            mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
+                                            + mLauncher.getTouchSlop(),
+                                    parentBounds.centerY(), parentBounds.centerX(),
+                                    parentBounds.centerY(), 10, true, GestureScope.INSIDE);
+                        }
+
                         return new Widget(mLauncher, widget);
                     }
                 }