Merge "Override Rect#contains() to include bottom right" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
index ed3ba92..6aa9619 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -26,5 +26,7 @@
   <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
 
   <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string>
+
+  <string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
 </resources>
 
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 ab3c71a..b6a8206 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
@@ -211,8 +211,11 @@
     }
 
     private void dispatchOnChange(boolean changed) {
-        PredictionState newState = changed ? parseLastState() :
-                (mPendingState == null ? mCurrentState : mPendingState);
+        PredictionState newState = changed
+                ? parseLastState()
+                : mPendingState != null && canApplyPredictions(mPendingState)
+                        ? mPendingState
+                        : mCurrentState;
         if (changed && mAppsView != null && !canApplyPredictions(newState)) {
             scheduleApplyPredictedApps(newState);
         } else {
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 e9f3534..1fe3643 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
@@ -25,10 +25,7 @@
 import android.app.prediction.AppPredictor;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
 import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Process;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,8 +33,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.CellLayout;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Hotseat;
@@ -48,7 +43,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.appprediction.ComponentKeyMapper;
@@ -57,12 +51,10 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.PredictionModel;
 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;
@@ -77,7 +69,6 @@
 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;
 
@@ -93,17 +84,6 @@
     private static final String TAG = "PredictiveHotseat";
     private static final boolean DEBUG = false;
 
-    //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
-    private static final int APPTARGET_ACTION_UNPIN = 4;
-
-    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;
@@ -116,13 +96,14 @@
 
     private DynamicItemCache mDynamicItemCache;
 
-    private final PredictionModel mPredictionModel;
+    private final HotseatPredictionModel mPredictionModel;
     private AppPredictor mAppPredictor;
     private AllAppsStore mAllAppsStore;
     private AnimatorSet mIconRemoveAnimators;
     private boolean mUIUpdatePaused = false;
     private boolean mRequiresCacheUpdate = true;
     private boolean mIsCacheEmpty;
+    private boolean mIsDestroyed = false;
 
     private HotseatEduController mHotseatEduController;
 
@@ -141,13 +122,13 @@
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
         mAllAppsStore = mLauncher.getAppsView().getAppsStore();
-        mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
+        LauncherAppState appState = LauncherAppState.getInstance(launcher);
+        mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
         mAllAppsStore.addUpdateListener(this);
         mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
         mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
         mHotseat.addOnAttachStateChangeListener(this);
-        mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
         if (mHotseat.isAttachedToWindow()) {
             onViewAttachedToWindow(mHotseat);
         }
@@ -260,6 +241,7 @@
      * Unregisters callbacks and frees resources
      */
     public void destroy() {
+        mIsDestroyed = true;
         mAllAppsStore.removeUpdateListener(this);
         mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
         mHotseat.removeOnAttachStateChangeListener(this);
@@ -292,100 +274,54 @@
         }
         if (mAppPredictor != null) {
             mAppPredictor.destroy();
+            mAppPredictor = null;
         }
-        mAppPredictor = apm.createAppPredictionSession(
-                new AppPredictionContext.Builder(mLauncher)
-                        .setUiSurface(PREDICTION_CLIENT)
-                        .setPredictedTargetCount(mHotSeatItemsCount)
-                        .setExtras(getAppPredictionContextExtra())
-                        .build());
         WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
-        mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(),
-                list -> {
-                    if (controllerRef.get() != null) {
-                        controllerRef.get().setPredictedApps(list);
-                    }
-                });
 
+
+        mPredictionModel.createBundle(bundle -> {
+            if (mIsDestroyed) return;
+            mAppPredictor = apm.createAppPredictionSession(
+                    new AppPredictionContext.Builder(mLauncher)
+                            .setUiSurface(PREDICTION_CLIENT)
+                            .setPredictedTargetCount(mHotSeatItemsCount)
+                            .setExtras(bundle)
+                            .build());
+            mAppPredictor.registerPredictionUpdates(
+                    mLauncher.getApplicationContext().getMainExecutor(),
+                    list -> {
+                        if (controllerRef.get() != null) {
+                            controllerRef.get().setPredictedApps(list);
+                        }
+                    });
+            mAppPredictor.requestPredictionUpdate();
+        });
         setPauseUIUpdate(false);
         if (!isEduSeen()) {
             mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
         }
-        mAppPredictor.requestPredictionUpdate();
     }
 
     /**
      * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
      */
-    public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+    public void showCachedItems(List<AppInfo> apps,  IntArray ranks) {
+        mIsCacheEmpty = apps.isEmpty();
         int count = Math.min(ranks.size(), apps.size());
         List<WorkspaceItemInfo> items = new ArrayList<>(count);
+        mComponentKeyMappers.clear();
         for (int i = 0; i < count; i++) {
             WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
+            ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
             preparePredictionInfo(item, ranks.get(i));
             items.add(item);
-        }
-        mComponentKeyMappers.clear();
-        for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
-            mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
+
+            mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
         }
         updateDependencies();
         bindItems(items, false, null);
     }
 
-    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++) {
-            View child = viewGroup.getChildAt(i);
-            if (isPinnedIcon(child)) {
-                WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
-                pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
-            }
-        }
-        return pinnedApps;
-    }
-
     private void setPredictedApps(List<AppTarget> appTargets) {
         mComponentKeyMappers.clear();
         StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
@@ -443,8 +379,11 @@
                 workspaceItemInfo.cellX, workspaceItemInfo.cellY);
         ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
         icon.pin(workspaceItemInfo);
-        AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
-        notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
+        AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
+        if (appTarget != null) {
+            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+                    AppTargetEvent.ACTION_PIN, workspaceItemInfo));
+        }
         mRequiresCacheUpdate = true;
     }
 
@@ -524,10 +463,9 @@
         mIconRemoveAnimators.start();
     }
 
-    private void notifyItemAction(AppTarget target, String location, int action) {
+    private void notifyItemAction(AppTargetEvent event) {
         if (mAppPredictor != null) {
-            mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target,
-                    action).setLaunchLocation(location).build());
+            mAppPredictor.notifyAppTargetEvent(event);
         }
     }
 
@@ -545,36 +483,35 @@
     /**
      * Unpins pinned app when it's converted into a folder
      */
-    public void folderCreatedFromWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
-        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-            return;
+    public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
+        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
+        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
+        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
+                    AppTargetEvent.ACTION_PIN, folderInfo));
         }
-        AppTarget target = getAppTargetFromItemInfo(info);
-        ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
-        ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
-                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
-
-        if (isInHotseat(folderInfo) && !getPinnedAppTargetsInViewGroup(hotseatVG).contains(
-                target)) {
-            notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
-        } else if (isInFirstPage(folderInfo) && !getPinnedAppTargetsInViewGroup(
-                firstScreenVG).contains(target)) {
-            notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+        // using folder info with isTrackedForPrediction as itemInfo.container is already changed
+        // to folder by this point
+        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
+                    AppTargetEvent.ACTION_UNPIN, folderInfo
+            ));
         }
     }
 
     /**
      * Pins workspace item created when all folder items are removed but one
      */
-    public void folderConvertedToWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
-        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-            return;
+    public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
+        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
+        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
+        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
+                    AppTargetEvent.ACTION_UNPIN, folderInfo));
         }
-        AppTarget target = getAppTargetFromItemInfo(info);
-        if (isInHotseat(info)) {
-            notifyItemAction(target, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
-        } else if (isInFirstPage(info)) {
-            notifyItemAction(target, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
+        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
+            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
+                    AppTargetEvent.ACTION_PIN, itemInfo));
         }
     }
 
@@ -585,29 +522,18 @@
         }
 
         ItemInfo dragInfo = mDragObject.dragInfo;
-        ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
-        ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
-                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
-
-        if (dragInfo instanceof WorkspaceItemInfo
-                && dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && dragInfo.getTargetComponent() != null) {
-            AppTarget appTarget = getAppTargetFromItemInfo(dragInfo);
-            if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
-                if (!getPinnedAppTargetsInViewGroup(hotseatVG).contains(appTarget)) {
-                    notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
-                }
+        if (mDragObject.isMoved()) {
+            AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
+            //always send pin event first to prevent AiAi from predicting an item moved within
+            // the same page
+            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
+                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+                        AppTargetEvent.ACTION_PIN, dragInfo));
             }
-            if (!isInFirstPage(dragInfo) && isInFirstPage(mDragObject.originalDragInfo)) {
-                if (!getPinnedAppTargetsInViewGroup(firstScreenVG).contains(appTarget)) {
-                    notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
-                }
-            }
-            if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
-                notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
-            }
-            if (isInFirstPage(dragInfo) && !isInFirstPage(mDragObject.originalDragInfo)) {
-                notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
+            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
+                    mDragObject.originalDragInfo)) {
+                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+                        AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
             }
         }
         mDragObject = null;
@@ -615,6 +541,7 @@
         mRequiresCacheUpdate = true;
     }
 
+
     @Nullable
     @Override
     public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
@@ -711,77 +638,4 @@
                 && ((WorkspaceItemInfo) view.getTag()).container
                 == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
     }
-
-    private static boolean isPinnedIcon(View view) {
-        if (!(view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo)) {
-            return false;
-        }
-        ItemInfo info = (ItemInfo) view.getTag();
-        return info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION && (
-                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
-    }
-
-    private static boolean isInHotseat(ItemInfo itemInfo) {
-        return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-    }
-
-    private static boolean isInFirstPage(ItemInfo itemInfo) {
-        return itemInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
-                && itemInfo.screenId == Workspace.FIRST_SCREEN_ID;
-    }
-
-    private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
-        if (info.getTargetComponent() == null) return null;
-        ComponentName cn = info.getTargetComponent();
-        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/hybridhotseat/HotseatPredictionModel.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
new file mode 100644
index 0000000..5a038d2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hybridhotseat;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.PredictionModel;
+import com.android.launcher3.model.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.ArrayList;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+/**
+ * Model helper for app predictions in workspace
+ */
+public class HotseatPredictionModel extends PredictionModel {
+    private static final String APP_LOCATION_HOTSEAT = "hotseat";
+    private static final String APP_LOCATION_WORKSPACE = "workspace";
+
+    private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
+    private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
+
+
+    public HotseatPredictionModel(Context context) { }
+
+    /**
+     * Creates and returns bundle using workspace items and cached items
+     */
+    public void createBundle(Consumer<Bundle> cb) {
+        LauncherAppState appState = LauncherAppState.getInstance(mContext);
+        appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                Bundle bundle = new Bundle();
+                ArrayList<AppTargetEvent> events = new ArrayList<>();
+                ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
+                workspaceItems.addAll(dataModel.appWidgets);
+                for (ItemInfo item : workspaceItems) {
+                    AppTarget target = getAppTargetFromInfo(item);
+                    if (target != null && !isTrackedForPrediction(item)) continue;
+                    events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
+                }
+                ArrayList<AppTarget> currentTargets = new ArrayList<>();
+                for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
+                    AppTarget target = getAppTargetFromInfo(itemInfo);
+                    if (target != null) currentTargets.add(target);
+                }
+                bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
+                bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
+                MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
+            }
+        });
+    }
+
+    /**
+     * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
+     * if item is not supported prediction
+     */
+    public 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),
+                    mContext.getPackageName(), info.user).build();
+        }
+        return null;
+    }
+
+
+    /**
+     * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
+     * location using {@link ItemInfo}
+     */
+    public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+        String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
+                info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                        ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
+                info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
+        return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
+    }
+
+    /**
+     * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
+     */
+    public static boolean isTrackedForPrediction(ItemInfo info) {
+        return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
+                info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+                        && info.screenId == Workspace.FIRST_SCREEN_ID);
+    }
+}
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 ad6a10b..95087ba 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
@@ -217,7 +217,9 @@
                 break;
             }
             case OVERVIEW_STATE_ORDINAL: {
-                DiscoveryBounce.showForOverviewIfNeeded(this);
+                RecentsView recentsView = getOverviewPanel();
+                DiscoveryBounce.showForOverviewIfNeeded(this,
+                        recentsView.getPagedOrientationHandler());
                 RecentsView rv = getOverviewPanel();
                 sendCustomAccessibilityEvent(
                         rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index a1cc60e..085b9b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -71,7 +71,7 @@
             builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
             mRecentsView.updateEmptyMessage();
         } else {
-            builder.getAnim().addListener(
+            builder.addListener(
                     AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
         }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 9f31608..9b4e890 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -19,6 +19,8 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.getMode;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
@@ -127,7 +129,11 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        boolean hideShelfTwoButtonLandscape = getMode(launcher) == TWO_BUTTONS &&
+                !recentsView.getPagedOrientationHandler().isLayoutNaturalToLauncher();
+        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
+                hideShelfTwoButtonLandscape) {
             return OVERVIEW_BUTTONS;
         } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f38ff10..dc8fb9e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -18,29 +18,26 @@
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
 import android.util.Log;
-import android.view.View;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
@@ -97,32 +94,25 @@
     @Override
     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets) {
-        if (mRecentsView != null) {
-            mRecentsView.setRunningTaskIconScaledDown(true);
+        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+        if (mActivity == null) {
+            Log.e(TAG, "Animation created, before activity");
+            return pa.buildAnim();
         }
 
-        AnimatorSet anim = new AnimatorSet();
-        anim.addListener(new AnimationSuccessListener() {
+        mRecentsView.setRunningTaskIconScaledDown(true);
+        pa.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
                 mActivityInterface.onSwipeUpToRecentsComplete();
-                if (mRecentsView != null) {
-                    mRecentsView.animateUpRunningTaskIconScale();
-                }
+                mRecentsView.animateUpRunningTaskIconScale();
             }
         });
-        if (mActivity == null) {
-            Log.e(TAG, "Animation created, before activity");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
-            return anim;
-        }
 
         DepthController depthController = mActivityInterface.getDepthController();
         if (depthController != null) {
-            anim.play(ObjectAnimator.ofFloat(depthController, DEPTH,
-                    BACKGROUND_APP.getDepth(mActivity),
-                    OVERVIEW.getDepth(mActivity))
-                    .setDuration(RECENTS_LAUNCH_DURATION));
+            pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
+                    OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
         }
 
         RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
@@ -132,53 +122,39 @@
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
         if (runningTaskTarget == null) {
             Log.e(TAG, "No closing app");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
-            return anim;
+            return pa.buildAnim();
         }
 
-        final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(
-            mRecentsView.getPagedViewOrientedState(), mActivity);
-
-        // At this point, the activity is already started and laid-out. Get the home-bounds
-        // relative to the screen using the rootView of the activity.
-        int loc[] = new int[2];
-        View rootView = mActivity.getRootView();
-        rootView.getLocationOnScreen(loc);
-        Rect homeBounds = new Rect(loc[0], loc[1],
-                loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
-        clipHelper.updateSource(homeBounds, runningTaskTarget);
-
-        Rect targetRect = new Rect();
-        mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
-                targetRect);
-        clipHelper.updateTargetRect(targetRect);
-        clipHelper.prepareAnimation(mActivity.getDeviceProfile());
+        TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
+        tsv.setDp(mActivity.getDeviceProfile());
+        tsv.setPreview(runningTaskTarget);
+        tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
+                mRecentsView.getPagedViewOrientedState().getDisplayRotation());
 
         TransformParams params = new TransformParams()
-                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
-        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
-        valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
-        valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-        valueAnimator.addUpdateListener((v) -> {
-            params.setProgress((float) v.getAnimatedValue()).setTargetSet(targets);
-            clipHelper.applyTransform(params);
-        });
+                .setTargetSet(targets)
+                .setSyncTransactionApplier(
+                        new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView()));
 
+        AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
+        params.setBaseAlphaCallback((t, a) -> recentsAlpha.value);
+
+        Interpolator taskInterpolator;
         if (targets.isAnimatingHome()) {
-            // If we are animating home, fade in the opening targets
-            RemoteAnimationTargets openingSet = new RemoteAnimationTargets(appTargets,
-                    wallpaperTargets, MODE_OPENING);
-
-            TransactionCompat transaction = new TransactionCompat();
-            valueAnimator.addUpdateListener((v) -> {
-                for (RemoteAnimationTargetCompat app : openingSet.apps) {
-                    transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
-                }
-                transaction.apply();
-            });
+            taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
+            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
+        } else {
+            // When animation from app to recents, the recents layer is drawn on top of the app. To
+            // prevent the overlap, we animate the task first and then quickly fade in the recents.
+            taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
+            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
+                    clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
         }
-        anim.play(valueAnimator);
-        return anim;
+
+        pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
+        tsv.addAppToOverviewAnim(pa, taskInterpolator);
+        pa.addOnFrameCallback(() -> tsv.apply(params));
+        return pa.buildAnim();
     }
 
     /**
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 bbee67c..614ba46 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -25,8 +23,6 @@
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -49,13 +46,16 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
@@ -377,18 +377,9 @@
             mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
         }
 
-        AnimatorSet anim = new AnimatorSet();
-        anim.setDuration(mTransitionDragLength * 2);
-        anim.setInterpolator(t -> t * mDragLengthFactor);
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
-                AnimatedFloat.VALUE,
-                mTaskViewSimulator.getFullScreenScale(), 1));
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
-                AnimatedFloat.VALUE,
-                BACKGROUND_APP.getOverviewFullscreenProgress(),
-                OVERVIEW.getOverviewFullscreenProgress()));
-        mWindowTransitionController =
-                AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
+        PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
+        mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
+        mWindowTransitionController = pa.createPlaybackController();
     }
 
     /**
@@ -398,7 +389,16 @@
 
     protected boolean onActivityInit(Boolean alreadyOnHome) {
         T createdActivity = mActivityInterface.getCreatedActivity();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
+        }
         if (createdActivity != null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
+            }
+            ((RecentsView) createdActivity.getOverviewPanel())
+                    .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+                            mDeviceState.getDisplayRotation());
             initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext));
         }
@@ -651,14 +651,11 @@
         }
 
         @Override
-        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
-                TransformParams params) {
-            if (app.mode == targetMode
-                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                builder.withMatrix(mMatrix)
-                        .withWindowCrop(mCropRect)
-                        .withCornerRadius(params.getCornerRadius());
-            }
+        public void onBuildTargetParams(
+                Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            builder.withMatrix(mMatrix)
+                    .withWindowCrop(mCropRect)
+                    .withCornerRadius(params.getCornerRadius());
         }
 
         @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 4ebfbd6..5dbf199 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -100,7 +100,9 @@
         super.onSwipeUpToRecentsComplete();
         Launcher launcher = getCreatedActivity();
         if (launcher != null) {
-            DiscoveryBounce.showForOverviewIfNeeded(launcher);
+            RecentsView recentsView = launcher.getOverviewPanel();
+            DiscoveryBounce.showForOverviewIfNeeded(launcher,
+                    recentsView.getPagedOrientationHandler());
         }
     }
 
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 82a3e79..a8fa630 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -276,6 +276,8 @@
         if (mActivity == activity) {
             return true;
         }
+        mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+                mDeviceState.getDisplayRotation());
         if (mActivity != null) {
             // The launcher may have been recreated as a result of device rotation.
             int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
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 acc7794..8bffc78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -540,8 +540,6 @@
         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).
@@ -618,21 +616,6 @@
             return;
         }
         mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
-        BaseDraggingActivity activity =
-                mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
-        }
-        if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
-            return;
-        }
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
-        }
-        ((RecentsView) activity.getOverviewPanel())
-            .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
-                mDeviceState.getDisplayRotation());
-        activity.getDragLayer().recreateControllers();
     }
 
     private InputConsumer newBaseConsumer(GestureState previousGestureState,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 2dc7f5f..abe4af4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -18,6 +18,7 @@
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
+
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
@@ -26,21 +27,22 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
+
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.MultiStateCallback;
@@ -49,18 +51,19 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * A dummy input consumer used when the device is still locked, e.g. from secure camera.
  */
 public class DeviceLockedInputConsumer implements InputConsumer,
-        RecentsAnimationCallbacks.RecentsAnimationListener {
+        RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
 
     private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
     private static int getFlagForIndex(int index, String name) {
@@ -83,19 +86,20 @@
     private final InputMonitorCompat mInputMonitorCompat;
 
     private final PointF mTouchDown = new PointF();
-    private final AppWindowAnimationHelper mAppWindowAnimationHelper;
     private final TransformParams mTransformParams;
-    private final Point mDisplaySize;
     private final MultiStateCallback mStateCallback;
 
+    private final Point mDisplaySize;
+    private final Matrix mMatrix = new Matrix();
+    private final float mMaxTranslationY;
+
     private VelocityTracker mVelocityTracker;
-    private float mProgress;
+    private final AnimatedFloat mProgress = new AnimatedFloat(this::applyTransform);
 
     private boolean mThresholdCrossed = false;
     private boolean mHomeLaunched = false;
 
     private RecentsAnimationController mRecentsAnimationController;
-    private RecentsAnimationTargets mRecentsAnimationTargets;
 
     public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
@@ -105,9 +109,10 @@
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
         mTouchSlopSquared = squaredTouchSlop(context);
-        mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
         mTransformParams = new TransformParams();
         mInputMonitorCompat = inputMonitorCompat;
+        mMaxTranslationY = context.getResources().getDimensionPixelSize(
+                R.dimen.device_locked_y_offset);
 
         // Do not use DeviceProfile as the user data might be locked
         mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
@@ -158,9 +163,7 @@
                     }
                 } else {
                     float dy = Math.max(mTouchDown.y - y, 0);
-                    mProgress = dy / mDisplaySize.y;
-                    mTransformParams.setProgress(mProgress);
-                    mAppWindowAnimationHelper.applyTransform(mTransformParams);
+                    mProgress.updateValue(dy / mDisplaySize.y);
                 }
                 break;
             }
@@ -189,20 +192,13 @@
                 // Is fling
                 dismissTask = velocityY < 0;
             } else {
-                dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
+                dismissTask = mProgress.value >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
             }
 
             // Animate back to fullscreen before finishing
-            ValueAnimator animator = ValueAnimator.ofFloat(mTransformParams.getProgress(), 0f);
+            ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0);
             animator.setDuration(100);
             animator.setInterpolator(Interpolators.ACCEL);
-            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                    mTransformParams.setProgress((float) valueAnimator.getAnimatedValue());
-                    mAppWindowAnimationHelper.applyTransform(mTransformParams);
-                }
-            });
             animator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -239,29 +235,15 @@
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
         mRecentsAnimationController = controller;
-        mRecentsAnimationTargets = targets;
-
-        Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
-        RemoteAnimationTargetCompat targetCompat = targets.findTask(
-                mGestureState.getRunningTaskId());
-        if (targetCompat != null) {
-            mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
-        }
-
-        // Offset the surface slightly
-        displaySize.offset(0, mContext.getResources().getDimensionPixelSize(
-                R.dimen.device_locked_y_offset));
-        mTransformParams.setTargetSet(mRecentsAnimationTargets);
-        mAppWindowAnimationHelper.updateTargetRect(displaySize);
-        mAppWindowAnimationHelper.applyTransform(mTransformParams);
-
+        mTransformParams.setTargetSet(targets);
+        applyTransform();
         mStateCallback.setState(STATE_TARGET_RECEIVED);
     }
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
         mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
+        mTransformParams.setTargetSet(null);
     }
 
     private void endRemoteAnimation() {
@@ -273,6 +255,20 @@
         }
     }
 
+    private void applyTransform() {
+        mTransformParams.setProgress(mProgress.value);
+        if (mTransformParams.getTargetSet() != null) {
+            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+        }
+    }
+
+    @Override
+    public void onBuildTargetParams(
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+        mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
+        builder.withMatrix(mMatrix);
+    }
+
     @Override
     public void onConsumerAboutToBeSwitched() {
         mStateCallback.setState(STATE_HANDLER_INVALIDATED);
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 a7979cc..d22755e 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
@@ -77,7 +77,6 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Rect mTmpRect = new Rect();
     private final RectF mTmpRectF = new RectF();
-    private final RectF mCurrentRectWithInsets = new RectF();
     private RecentsOrientedState mOrientedState;
     // Corner radius of windows, in pixels
     private final float mWindowCornerRadius;
@@ -88,9 +87,6 @@
     // Whether or not to actually use the rounded cornders on windows
     private boolean mUseRoundedCornersOnWindows;
 
-    // Corner radius currently applied to transformed window.
-    private float mCurrentCornerRadius;
-
     public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
         Resources res = context.getResources();
         mOrientedState = orientedState;
@@ -100,10 +96,6 @@
         mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
     }
 
-    public AppWindowAnimationHelper(Context context) {
-        this(null, context);
-    }
-
     private void updateSourceStack(RemoteAnimationTargetCompat target) {
         mSourceInsets.set(target.contentInsets);
         mSourceStackBounds.set(target.screenSpaceBounds);
@@ -112,15 +104,6 @@
         mSourceStackBounds.offsetTo(target.position.x, target.position.y);
     }
 
-    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
-        updateSourceStack(target);
-        updateHomeBounds(homeStackBounds);
-    }
-
-    public void updateHomeBounds(Rect homeStackBounds) {
-        mHomeStackBounds.set(homeStackBounds);
-    }
-
     public void updateTargetRect(Rect targetRect) {
         mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
                 mSourceStackBounds.width() - mSourceInsets.right,
@@ -205,7 +188,6 @@
                     cornerRadius = mapRange(boundToRange(params.getProgress(), 0, 1),
                             windowCornerRadius, mTaskCornerRadius);
                 }
-                mCurrentCornerRadius = cornerRadius;
             }
 
             builder.withMatrix(mTmpMatrix)
@@ -241,11 +223,6 @@
                 mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
     }
 
-    public RectF getCurrentRectWithInsets() {
-        mTmpMatrix.mapRect(mCurrentRectWithInsets, mCurrentClipRectF);
-        return mCurrentRectWithInsets;
-    }
-
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
             @Nullable RemoteAnimationTargetCompat target) {
         BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
@@ -317,8 +294,4 @@
         return mTargetRect;
     }
 
-    public float getCurrentCornerRadius() {
-        return mCurrentCornerRadius;
-    }
-
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 32fc0de..3e0daaf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -212,12 +212,13 @@
     }
 
     private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
-        PendingAnimation builder = new PendingAnimation(duration, mAnimators);
+        PendingAnimation builder = new PendingAnimation(duration);
         launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
         builder.setFloat(
                 launcher.getDragLayer().getOverviewScrim(),
                 OverviewScrim.SCRIM_PROGRESS,
                 state.getOverviewScrimAlpha(launcher),
                 ACCEL_DEACCEL);
+        mAnimators.play(builder.buildAnim());
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index a3db940..f4f7e9c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -20,6 +20,7 @@
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
+import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PointF;
@@ -29,6 +30,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
@@ -145,6 +147,14 @@
     }
 
     /**
+     * Adds animation for all the components corresponding to transition from an app to overview
+     */
+    public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+        pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
+        pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator);
+    }
+
+    /**
      * Returns the current clipped/visible window bounds in the window coordinate space
      */
     public RectF getCurrentCropRect() {
@@ -250,14 +260,11 @@
     }
 
     @Override
-    public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
-            int targetMode, TransformParams params) {
-        if (app.mode == targetMode
-                && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-            builder.withMatrix(mMatrix)
-                    .withWindowCrop(mTmpCropRect)
-                    .withCornerRadius(getCurrentCornerRadius());
-        }
+    public void onBuildTargetParams(
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+        builder.withMatrix(mMatrix)
+                .withWindowCrop(mTmpCropRect)
+                .withCornerRadius(getCurrentCornerRadius());
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index 83b64db..d837e54 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.util;
 
 import android.graphics.RectF;
+import android.util.FloatProperty;
 
 import androidx.annotation.Nullable;
 
@@ -29,6 +30,19 @@
 
 public class TransformParams {
 
+    public static FloatProperty<TransformParams> PROGRESS =
+            new FloatProperty<TransformParams>("progress") {
+        @Override
+        public void setValue(TransformParams params, float v) {
+            params.setProgress(v);
+        }
+
+        @Override
+        public Float get(TransformParams params) {
+            return params.getProgress();
+        }
+    };
+
     private float mProgress;
     private @Nullable RectF mCurrentRect;
     private float mTargetAlpha;
@@ -176,10 +190,6 @@
         return mTargetSet;
     }
 
-    public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
-        return mSyncTransactionApplier;
-    }
-
     public void applySurfaceParams(SurfaceParams[] params) {
         if (mSyncTransactionApplier != null) {
             mSyncTransactionApplier.scheduleApply(params);
@@ -199,7 +209,15 @@
 
     public interface BuilderProxy {
 
-        void onBuildParams(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, int targetMode, TransformParams params);
+        default void onBuildParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, int targetMode, TransformParams params) {
+            if (app.mode == targetMode
+                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                onBuildTargetParams(builder, app, params);
+            }
+        }
+
+        default void onBuildTargetParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, TransformParams params) { }
     }
 }
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 253e83c..3fc235c 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
@@ -2190,6 +2190,10 @@
      */
     public void setModalStateEnabled(boolean isModalState) { }
 
+    public BaseActivityInterface getSizeStrategy() {
+        return mSizeStrategy;
+    }
+
     /**
      * Used to register callbacks for when our empty message state changes.
      *
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 8e14bbb..016ffea 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -322,7 +322,7 @@
         pw.println("  gestureID=" + mGestureId);
         pw.println("  runningTask=" + mRunningTask);
         pw.println("  endTarget=" + mEndTarget);
-        pw.println("  lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+        pw.println("  lastAppearedTaskTargetId=" + getLastAppearedTaskId());
         pw.println("  lastStartedTaskId=" + mLastStartedTaskId);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 8ac15e8..a892ddc 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -73,8 +73,6 @@
         NavigationModeChangeListener,
         DefaultDisplay.DisplayInfoChangeListener {
 
-    private static final String TAG = "RecentsAnimationDeviceState";
-
     private final Context mContext;
     private final SysUINavigationMode mSysUiNavMode;
     private final DefaultDisplay mDefaultDisplay;
@@ -97,7 +95,6 @@
         @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();
             }
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 5fa6bc7..f90df45 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -68,7 +68,7 @@
     }
 
     public boolean isAnimatingHome() {
-        for (RemoteAnimationTargetCompat target : apps) {
+        for (RemoteAnimationTargetCompat target : unfilteredApps) {
             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                 return true;
             }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index e03f4b8..4c47d7f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.content.ContentResolver;
@@ -53,6 +54,7 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.SysUINavigationMode;
 
 import java.lang.annotation.Retention;
 import java.util.function.IntConsumer;
@@ -118,6 +120,9 @@
             MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
                     | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
 
+    private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
+            newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final SharedPreferences mSharedPrefs;
@@ -163,13 +168,7 @@
         if (isFixedRotationTransformEnabled(context)) {
             mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
         }
-        if (mOrientationListener.canDetectOrientation()) {
-            mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
-        }
-
-        // initialize external flags
-        updateAutoRotateSetting();
-        updateHomeRotationSetting();
+        initFlags();
     }
 
     /**
@@ -273,6 +272,18 @@
                 mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
     }
 
+    private void initFlags() {
+        SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
+        if (mOrientationListener.canDetectOrientation() &&
+                currentMode != TWO_BUTTONS) {
+            mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
+        }
+
+        // initialize external flags
+        updateAutoRotateSetting();
+        updateHomeRotationSetting();
+    }
+
     /**
      * Initializes any system values and registers corresponding change listeners. It must be
      * paired with {@link #destroyListeners()} call
@@ -283,9 +294,11 @@
             mContentResolver.registerContentObserver(
                     Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
                     false, mSystemAutoRotateObserver);
+            SysUINavigationMode.Mode currentMode =
+                    SysUINavigationMode.INSTANCE.get(mContext)
+                            .addModeChangeListener(mNavModeChangeListener);
         }
-        updateAutoRotateSetting();
-        updateHomeRotationSetting();
+        initFlags();
     }
 
     /**
@@ -295,6 +308,8 @@
         if (isMultipleOrientationSupportedByDevice()) {
             mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+            SysUINavigationMode.INSTANCE.get(mContext)
+                    .removeModeChangeListener(mNavModeChangeListener);
         }
         setRotationWatcherEnabled(false);
     }
diff --git a/res/values/config.xml b/res/values/config.xml
index 603dc91..4cbc597 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,6 +69,7 @@
     <string name="app_launch_tracker_class" translatable="false"></string>
     <string name="test_information_handler_class" translatable="false"></string>
     <string name="launcher_activity_logic_class" translatable="false"></string>
+    <string name="prediction_model_class" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 0b0983c..c1aed98 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -110,6 +110,18 @@
 
             return res;
         }
+
+
+        /**
+         * This is used to determine if an object is dropped at a different location than it was
+         * dragged from
+         */
+        public boolean isMoved() {
+            return dragInfo.cellX != originalDragInfo.cellX
+                    || dragInfo.cellY != originalDragInfo.cellY
+                    || dragInfo.screenId != originalDragInfo.screenId
+                    || dragInfo.container != originalDragInfo.container;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index faa18b8..9a66d32 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -91,6 +91,9 @@
     @Override
     public void onViewAdded(View child) {
         super.onViewAdded(child);
+        if (!isAttachedToWindow()) {
+            return;
+        }
         setFrameLayoutChildInsets(child, mInsets, new Rect());
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 14e604d..53e5274 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -129,7 +129,7 @@
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
-        mPredictionModel = new PredictionModel(mContext);
+        mPredictionModel = PredictionModel.newInstance(mContext);
     }
 
     protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 5397942..b4ff5ea 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.OnboardingPrefs;
 
 /**
@@ -153,16 +154,19 @@
         new DiscoveryBounce(launcher, 0).show(HOTSEAT);
     }
 
-    public static void showForOverviewIfNeeded(Launcher launcher) {
-        showForOverviewIfNeeded(launcher, true);
+    public static void showForOverviewIfNeeded(Launcher launcher,
+                                               PagedOrientationHandler orientationHandler) {
+        showForOverviewIfNeeded(launcher, true, orientationHandler);
     }
 
-    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
+    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
+                                                PagedOrientationHandler orientationHandler) {
         OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
         if (!launcher.isInState(OVERVIEW)
                 || !launcher.hasBeenResumed()
                 || launcher.isForceInvisible()
                 || launcher.getDeviceProfile().isVerticalBarLayout()
+                || !orientationHandler.isLayoutNaturalToLauncher()
                 || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,7 +174,8 @@
         }
 
         if (withDelay) {
-            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
+            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
+                    orientationHandler), DELAY_MS);
             return;
         } else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
             // TODO: Move these checks to the top and call this method after invalidate handler.
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 80b6a5a..a6bc6cf 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -60,6 +60,14 @@
     }
 
     @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+            return false;
+        }
+        return super.onTouchEvent(ev);
+    }
+
+    @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
         mLauncher.getAllAppsController()
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 740f7f2..0f04104 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -51,12 +52,8 @@
     private ValueAnimator mProgressAnimator;
 
     public PendingAnimation(long  duration) {
-        this(duration, new AnimatorSet());
-    }
-
-    public PendingAnimation(long  duration, AnimatorSet targetSet) {
         mDuration = duration;
-        mAnim = targetSet;
+        mAnim = new AnimatorSet();
     }
 
     /**
@@ -129,13 +126,32 @@
     public void addOnFrameCallback(Runnable runnable) {
         if (mProgressAnimator == null) {
             mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
-            add(mProgressAnimator);
         }
 
         mProgressAnimator.addUpdateListener(anim -> runnable.run());
     }
 
-    public AnimatorSet getAnim() {
+    /**
+     * @see AnimatorSet#addListener(AnimatorListener)
+     */
+    public void addListener(Animator.AnimatorListener listener) {
+        mAnim.addListener(listener);
+    }
+
+    /**
+     * Creates and returns the underlying AnimatorSet
+     */
+    public AnimatorSet buildAnim() {
+        // Add progress animation to the end, so that frame callback is called after all the other
+        // animation update.
+        if (mProgressAnimator != null) {
+            add(mProgressAnimator);
+            mProgressAnimator = null;
+        }
+        if (mAnimHolders.isEmpty()) {
+            // Add a dummy animation to that the duration is respected
+            add(ValueAnimator.ofFloat(0, 1));
+        }
         return mAnim;
     }
 
@@ -143,7 +159,7 @@
      * Creates a controller for this animation
      */
     public AnimatorPlaybackController createPlaybackController() {
-        return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
+        return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index b240f0b..475305f 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -35,50 +35,50 @@
     }
 
     public enum LauncherEvent implements EventEnum {
-        @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
+        @UiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
         LAUNCHER_APP_LAUNCH_TAP(338),
 
-        @LauncherUiEvent(doc = "Task launched from overview using TAP")
+        @UiEvent(doc = "Task launched from overview using TAP")
         LAUNCHER_TASK_LAUNCH_TAP(339),
 
-        @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
+        @UiEvent(doc = "Task launched from overview using SWIPE DOWN")
         LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
 
-        @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
+        @UiEvent(doc = "TASK dismissed from overview using SWIPE UP")
         LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
 
-        @LauncherUiEvent(doc = "User dragged a launcher item")
+        @UiEvent(doc = "User dragged a launcher item")
         LAUNCHER_ITEM_DRAG_STARTED(383),
 
-        @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
+        @UiEvent(doc = "A dragged launcher item is successfully dropped")
         LAUNCHER_ITEM_DROP_COMPLETED(385),
 
-        @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
+        @UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
                 + "resulting in a new folder creation")
         LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
 
-        @LauncherUiEvent(doc = "User action resulted in or manually updated the folder label to "
+        @UiEvent(doc = "User action resulted in or manually updated the folder label to "
                 + "new/same value.")
         LAUNCHER_FOLDER_LABEL_UPDATED(460),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
 
-        @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+        @UiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
                 + " button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
         LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
 
-        @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+        @UiEvent(doc = "User completed uninstalling the package after dropping on "
                 + "the icon onto 'Uninstall' button in the target bar")
         LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
 
-        @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+        @UiEvent(doc = "User cancelled uninstalling the package after dropping on "
                 + "the icon onto 'Uninstall' button in the target bar")
         LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
         // ADD MORE
diff --git a/src/com/android/launcher3/logging/LauncherUiEvent.java b/src/com/android/launcher3/logging/UiEvent.java
similarity index 80%
rename from src/com/android/launcher3/logging/LauncherUiEvent.java
rename to src/com/android/launcher3/logging/UiEvent.java
index 4507ff7..20d6c72 100644
--- a/src/com/android/launcher3/logging/LauncherUiEvent.java
+++ b/src/com/android/launcher3/logging/UiEvent.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.launcher3.logging;
 
 import static java.lang.annotation.ElementType.FIELD;
@@ -23,8 +24,11 @@
 
 @Retention(SOURCE)
 @Target(FIELD)
-public @interface LauncherUiEvent {
-    /** An explanation, suitable for Android analysts, of the UI event that this log represents. */
+//  Copy of frameworks/base/core/java/com/android/internal/logging/UiEvent.java
+public @interface UiEvent {
+
+    /**
+     * An explanation, suitable for Android analysts, of the UI event that this log represents.
+     */
     String doc();
 }
-
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 1465100..ab921ea 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -253,8 +253,8 @@
         }
 
         private void bindPredictedItems(IntArray ranks, final Executor executor) {
-            executeCallbacksTask(
-                    c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
+            ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
+            executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
         }
 
         protected void executeCallbacksTask(CallbackTask task, Executor executor) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9e6282e..d05d70b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -45,6 +45,8 @@
 import android.util.MutableInt;
 import android.util.TimingLogger;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
@@ -850,12 +852,11 @@
         }
     }
 
-    private List<AppInfo> loadCachedPredictions() {
+    @WorkerThread
+    private void loadCachedPredictions() {
         synchronized (mBgDataModel) {
             List<ComponentKey> componentKeys =
                     mApp.getPredictionModel().getPredictionComponentKeys();
-            List<AppInfo> results = new ArrayList<>();
-            if (componentKeys == null) return results;
             List<LauncherActivityInfo> l;
             mBgDataModel.cachedPredictedItems.clear();
             for (ComponentKey key : componentKeys) {
@@ -866,7 +867,6 @@
                 mBgDataModel.cachedPredictedItems.add(info);
                 mIconCache.getTitleAndIcon(info, false);
             }
-            return results;
         }
     }
 
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
index 6aa41eb..1429843 100644
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -14,80 +14,111 @@
  * limitations under the License.
  */
 package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.UserHandle;
 
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
- * Model helper for app predictions in workspace
+ * Model Helper for app predictions
  */
-public class PredictionModel {
+public class PredictionModel implements ResourceBasedOverride {
+
     private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
     private static final int MAX_CACHE_ITEMS = 5;
 
-    private final Context mContext;
-    private final SharedPreferences mDevicePrefs;
-    private ArrayList<ComponentKey> mCachedComponentKeys;
+    protected Context mContext;
+    private SharedPreferences mDevicePrefs;
+    private UserCache mUserCache;
 
-    public PredictionModel(Context context) {
-        mContext = context;
-        mDevicePrefs = Utilities.getDevicePrefs(mContext);
+
+    /**
+     * Retrieve instance of this object that can be overridden in runtime based on the build
+     * variant of the application.
+     */
+    public static PredictionModel newInstance(Context context) {
+        PredictionModel model = Overrides.getObject(PredictionModel.class, context,
+                R.string.prediction_model_class);
+        model.init(context);
+        return model;
     }
 
+    protected void init(Context context) {
+        mContext = context;
+        mDevicePrefs = Utilities.getDevicePrefs(mContext);
+        mUserCache = UserCache.INSTANCE.get(mContext);
+
+    }
     /**
      * Formats and stores a list of component key in device preferences.
      */
+    @AnyThread
     public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
-        StringBuilder builder = new StringBuilder();
-        int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
-        for (int i = 0; i < count; i++) {
-            builder.append(componentKeys.get(i));
-            builder.append("\n");
-        }
-        mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
-        mCachedComponentKeys = null;
+        MODEL_EXECUTOR.execute(() -> {
+            StringBuilder builder = new StringBuilder();
+            int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
+            for (int i = 0; i < count; i++) {
+                builder.append(serializeComponentKeyToString(componentKeys.get(i)));
+                builder.append("\n");
+            }
+            mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
+        });
     }
 
     /**
      * parses and returns ComponentKeys saved by
      * {@link PredictionModel#cachePredictionComponentKeys(List)}
      */
+    @WorkerThread
     public List<ComponentKey> getPredictionComponentKeys() {
-        if (mCachedComponentKeys == null) {
-            mCachedComponentKeys = new ArrayList<>();
-
-            String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
-            for (String line : cachedBlob.split("\n")) {
-                ComponentKey key = ComponentKey.fromString(line);
-                if (key != null) {
-                    mCachedComponentKeys.add(key);
-                }
+        Preconditions.assertWorkerThread();
+        ArrayList<ComponentKey> items = new ArrayList<>();
+        String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
+        for (String line : cachedBlob.split("\n")) {
+            ComponentKey key = getComponentKeyFromSerializedString(line);
+            if (key != null) {
+                items.add(key);
             }
+
         }
-        return mCachedComponentKeys;
+        return items;
     }
 
-    /**
-     * Remove uninstalled applications from model
-     */
-    public void removePackage(String pkgName, UserHandle user, ArrayList<AppInfo> ids) {
-        for (int i = ids.size() - 1; i >= 0; i--) {
-            AppInfo info = ids.get(i);
-            if (info.user.equals(user) && pkgName.equals(info.componentName.getPackageName())) {
-                ids.remove(i);
-            }
+    private String serializeComponentKeyToString(ComponentKey componentKey) {
+        long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
+        return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
+    }
+
+    private ComponentKey getComponentKeyFromSerializedString(String str) {
+        int sep = str.indexOf('#');
+        if (sep < 0 || (sep + 1) >= str.length()) {
+            return null;
         }
-        cachePredictionComponentKeys(getPredictionComponentKeys().stream()
-                .filter(cn -> !(cn.user.equals(user) && cn.componentName.getPackageName().equals(
-                        pkgName))).collect(Collectors.toList()));
+        ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
+        if (componentName == null) {
+            return null;
+        }
+        try {
+            long serialNumber = Long.parseLong(str.substring(sep + 1));
+            UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
+            return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
+        } catch (NumberFormatException ex) {
+            return null;
+        }
     }
 }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 4447166..97dc052 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -239,7 +239,7 @@
                 ? fromState.getTransitionDuration(mActivity)
                 : state.getTransitionDuration(mActivity);
         prepareForAtomicAnimation(fromState, state, mConfig);
-        AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
+        AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
         if (onCompleteRunnable != null) {
             animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
         }
@@ -267,7 +267,7 @@
         for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
             handler.setStateWithAnimation(toState, config, builder);
         }
-        return builder.getAnim();
+        return builder.buildAnim();
     }
 
     /**
@@ -309,7 +309,7 @@
         for (StateHandler handler : getStateHandlers()) {
             handler.setStateWithAnimation(state, mConfig, builder);
         }
-        builder.getAnim().addListener(new AnimationSuccessListener() {
+        builder.addListener(new AnimationSuccessListener() {
 
             @Override
             public void onAnimationStart(Animator animation) {
@@ -325,7 +325,7 @@
                 onStateTransitionEnd(state);
             }
         });
-        mConfig.setAnimation(builder.getAnim(), state);
+        mConfig.setAnimation(builder.buildAnim(), state);
         return builder;
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 14212be..546ff0b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -362,7 +362,7 @@
     public void checkForAnomaly() {
         final String systemAnomalyMessage = getSystemAnomalyMessage();
         if (systemAnomalyMessage != null) {
-            Assert.fail(formatSystemHealthMessage(closeEvents(
+            Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                     "http://go/tapl : Tests are broken by a non-Launcher system error: "
                             + systemAnomalyMessage, false)));
         }
@@ -424,7 +424,7 @@
         return message;
     }
 
-    private String closeEvents(String message, boolean checkEvents) {
+    private String formatErrorWithEvents(String message, boolean checkEvents) {
         if (sCheckingEvents) {
             sCheckingEvents = false;
             if (checkEvents) {
@@ -436,9 +436,12 @@
                 sEventChecker.finishNoWait();
             }
         }
-        // b/156287114
+
         try {
-            log("Input: " + mDevice.executeShellCommand("dumpsys input"));
+            Log.e("b/156287114", "Input:");
+            for (String line : mDevice.executeShellCommand("dumpsys input").split("\\n")) {
+                Log.d("b/156287114", line);
+            }
         } catch (IOException e) {
             e.printStackTrace();
         }
@@ -451,7 +454,7 @@
 
     private void fail(String message) {
         checkForAnomaly();
-        Assert.fail(formatSystemHealthMessage(closeEvents(
+        Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                 "http://go/tapl : " + getContextDescription() + message
                         + " (visible state: " + getVisibleStateMessage() + ")", true)));
     }
@@ -629,8 +632,6 @@
      * @return the Workspace object.
      */
     public Workspace pressHome() {
-        mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
-                e -> Log.d("b/155926212", e.toString()));
         try (LauncherInstrumentation.Closable e = eventsCheck()) {
             waitForLauncherInitialized();
             // Click home, then wait for any accessibility event, then wait until accessibility
@@ -639,9 +640,7 @@
             // otherwise waitForIdle may return immediately in case when there was a big enough
             // pause in accessibility events prior to pressing Home.
             final String action;
-            Log.d("b/155926212", "Before isLauncherVisible()");
             final boolean launcherWasVisible = isLauncherVisible();
-            Log.d("b/155926212", "After isLauncherVisible(): " + launcherWasVisible);
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
                 checkForAnomaly();
 
@@ -697,8 +696,6 @@
                     "performed action to switch to Home - " + action)) {
                 return getWorkspace();
             }
-        } finally {
-            mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(null);
         }
     }
 
@@ -1312,7 +1309,8 @@
                 if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
                 checkForAnomaly();
                 Assert.fail(
-                        formatSystemHealthMessage(closeEvents("Launcher crashed", false)));
+                        formatSystemHealthMessage(
+                                formatErrorWithEvents("Launcher crashed", false)));
             }
 
             if (sCheckingEvents) {
@@ -1320,6 +1318,16 @@
                 if (mCheckEventsForSuccessfulGestures) {
                     final String message = sEventChecker.verify(WAIT_TIME_MS, true);
                     if (message != null) {
+                        try {
+                            Log.e("b/156287114", "Input:");
+                            for (String line : mDevice.executeShellCommand("dumpsys input").split(
+                                    "\\n")) {
+                                Log.d("b/156287114", line);
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+
                         checkForAnomaly();
                         Assert.fail(formatSystemHealthMessage(
                                 "http://go/tapl : successful gesture produced " + message));