Merging  ub-launcher3-qt-dev, build 5505393

Test: Manual
Bug:111411630 Dark theme popup blends in with the normal background
Bug:113952590 [STOP SHIP BUG] WM Experiment: Slim the navigation bar
Bug:114136250 Have a more spartan RecentsActivity on android go
Bug:117106893 Enable all @Ignore-d Launcher tests
Bug:118678948 A11Y: TalkBack: After adding shortcut app icon on the homescreen, sometimes shortcuts app icons are not accessible with TalkBack.
Bug:122700646 Launcher crashed after uninstalling app
Bug:123985787 Respect dialogCornerRadius for all apps / widgets / settings / shortcut menu / dwb / task / overview (everything)
Bug:126229665 Launcher home screen should properly use Headline/ body font
Bug:126450861 Tab structure update and tips for Theme Picker
Bug:126587956 Create an app to use with Launcher testing
Bug:126596502 [touchflow] Use platform long press in launcher
Bug:126738409 Icon packs for launcher
Bug:126822309 Launcher > Overview > "No recent item" is not using theme font
Bug:127689526 [icons] hourglass_bottom and hourglass_top
Bug:127840207 [logging] quickswitch usage
Bug:128531133 Make TAPL diagnostics clearer
Bug:128649657 Define overlayable.xml for icon packs
Bug:128808922 [QT-QP1A] Click to change orientation in GCA secure mode will lead to unlock screen.
Bug:128857973 With dark display and black accent device theme, App list header not visible on all apps screen.
Bug:129145824 When predictions are disabled, the hotseat does not appear in quickstep
Bug:129146717 Do not allow overlays to change attribute format
Bug:129297464 [Gesture Nav] Exclude edges from most Launcher / Overview states.
Bug:129328259 [Regression] Suspended app icons turn full colour during open animation
Bug:129421279 FloatingIconView should be removed when user interacts with launcher
Bug:129434166 Lab-only flake: drag to workspace doesn't happen
Bug:129785904 [Gesture Nav] Verify that gestures are disabled appropriately in transient states
Bug:129955150 Launcher settings' icon is not using consistent grey
Bug:129985827 [Fully Gestural Navigation] Delay Recents animation when swiping up
Bug:130053407 [Tests broken] testLearn: settings is not present in the prediction row
Bug:130160876 Full gesture navigation with fast switcher shows part of "clear all" message
Bug:130415177 Launcher displays app as suspended when it is not if setPackagesSuspended is called multiple times
Bug:130440957 Recents Go view visibility not updating while loading UI is up
Bug:130442915 Jank observed while navigating from long press app shortcut.
Bug:130562632 DefaultLayoutProviderTest.testCustomProfileLoaded_with_folder is flaking
Bug:130580680 Recents Go does not load views right after boot
Bug:130635650 Recents Go thumbnails not updating on app => recents transition
Bug:130735711 Should fix overview shortcut in Recents Go landscape mode
Bug:130739123 Tests failure: Attempt to invoke virtual method 'int com.android.launcher3.views.BaseDragLayer.getChildCount()' on a null object reference
Bug:130740246 Recents Go crashes from ViewTreeObserver being dead
Bug:130746661 Fix NPE for getting task from loading task UI in Recents Go
Bug:130820737 Recents Go remove animation doesn't always work
Bug:130914022 Add a test for DWB toast
Bug:130917995 onConfigChanged when apps are auto installed
Bug:131095241 Recents Go should support landscape app => landscape thumbnail transition
Bug:131102753 crosshatch_svelte doesn't boot on ToT master on 4/22 because of FATAL exception in launcher3
Bug:131170582 [Flaky test] TaplTestsLauncher3.testDragShortcut
Bug:131250710 ControlType.CLEAL_ALL_BUTTON logging is gone
Bug:78240090 (Quickstep) Swiping down on the shelf while scrolling carousel
Change-Id: I4ef9beb24fab6be4fcff44b330e30d486fc01f03
diff --git a/Android.mk b/Android.mk
index a817415..6568a26 100644
--- a/Android.mk
+++ b/Android.mk
@@ -251,7 +251,7 @@
 LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_PRODUCT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3GoIconRecents
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
@@ -297,7 +297,8 @@
 
 LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+LOCAL_PRODUCT_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3QuickStepGo
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
diff --git a/res/layout/hint_container.xml b/go/quickstep/res/drawable/default_thumbnail.xml
similarity index 76%
rename from res/layout/hint_container.xml
rename to go/quickstep/res/drawable/default_thumbnail.xml
index 75aa913..0a2dbf0 100644
--- a/res/layout/hint_container.xml
+++ b/go/quickstep/res/drawable/default_thumbnail.xml
@@ -14,4 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<merge/>
\ No newline at end of file
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@android:color/darker_gray"/>
+    <corners android:radius="2dp"/>
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/layout/hint_container.xml b/go/quickstep/res/drawable/empty_content_box.xml
similarity index 74%
rename from quickstep/recents_ui_overrides/res/layout/hint_container.xml
rename to go/quickstep/res/drawable/empty_content_box.xml
index f8723fc..a488388 100644
--- a/quickstep/recents_ui_overrides/res/layout/hint_container.xml
+++ b/go/quickstep/res/drawable/empty_content_box.xml
@@ -14,10 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.hints.ChipsContainer
+<shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/chip_hint_height"
-    android:layout_gravity="bottom"
-    android:gravity="center_horizontal"
-    android:orientation="horizontal"/>
\ No newline at end of file
+    android:shape="rectangle">
+    <solid android:color="@android:color/transparent"/>
+    <stroke android:color="@android:color/white" android:width="4px"/>
+    <corners android:radius="2dp"/>
+</shape>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/clear_all_button.xml b/go/quickstep/res/layout/clear_all_button.xml
new file mode 100644
index 0000000..be76d53
--- /dev/null
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.quickstep.views.ClearAllItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clear_all_item_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <Button
+        android:id="@+id/clear_all_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginVertical="16dp"
+        android:layout_gravity="center_horizontal"
+        android:paddingHorizontal="32dp"
+        android:background="@drawable/clear_all_button"
+        android:gravity="center"
+        android:text="@string/recents_clear_all"
+        android:textAllCaps="false"
+        android:textColor="@color/clear_all_button_text"
+        android:textSize="14sp"/>
+</com.android.quickstep.views.ClearAllItemView>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index fddb1d3..6fb7e19 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -19,31 +19,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
-    <LinearLayout
-        android:id="@+id/recent_task_content_view"
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recent_task_recycler_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:visibility="gone">
-        <androidx.recyclerview.widget.RecyclerView
-            android:id="@+id/recent_task_recycler_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:scrollbars="none"/>
-        <Button
-            android:id="@+id/clear_all_button"
-            android:layout_width="@dimen/clear_all_button_width"
-            android:layout_height="@dimen/clear_all_button_height"
-            android:layout_gravity="center_horizontal"
-            android:layout_marginVertical="@dimen/task_item_half_vert_margin"
-            android:background="@drawable/clear_all_button"
-            android:gravity="center"
-            android:text="@string/recents_clear_all"
-            android:textAllCaps="false"
-            android:textColor="@color/clear_all_button_text"
-            android:textSize="14sp"/>
-    </LinearLayout>
+        android:scrollbars="none"/>
     <TextView
         android:id="@+id/recent_task_empty_view"
         android:layout_width="match_parent"
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index 048e9c5..1483d4c 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -17,26 +17,23 @@
 <com.android.quickstep.views.TaskItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:orientation="horizontal">
-    <FrameLayout
+    <com.android.quickstep.views.TaskThumbnailIconView
         android:id="@+id/task_icon_and_thumbnail"
-        android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
-        android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
-        android:layout_gravity="center_vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:layout_marginHorizontal="8dp"
-        android:layout_marginVertical="@dimen/task_item_half_vert_margin">
+        android:layout_marginTop="16dp">
         <ImageView
             android:id="@+id/task_thumbnail"
-            android:layout_width="@dimen/task_thumbnail_width"
-            android:layout_height="@dimen/task_thumbnail_height"
-            android:layout_gravity="top|start"/>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
         <ImageView
             android:id="@+id/task_icon"
-            android:layout_width="@dimen/task_icon_size"
-            android:layout_height="@dimen/task_icon_size"
-            android:layout_gravity="bottom|end"/>
-    </FrameLayout>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </com.android.quickstep.views.TaskThumbnailIconView>
     <TextView
         android:id="@+id/task_label"
         android:layout_width="wrap_content"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
deleted file mode 100644
index e2fa387..0000000
--- a/go/quickstep/res/values/dimens.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <dimen name="task_item_half_vert_margin">8dp</dimen>
-    <dimen name="task_thumbnail_and_icon_view_size">60dp</dimen>
-    <dimen name="task_thumbnail_height">60dp</dimen>
-    <dimen name="task_thumbnail_width">36dp</dimen>
-    <dimen name="task_icon_size">36dp</dimen>
-
-    <dimen name="clear_all_button_width">106dp</dimen>
-    <dimen name="clear_all_button_height">36dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 6730e97..d20910f 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -51,6 +52,9 @@
     public void onStateEnabled(Launcher launcher) {
         IconRecentsView recentsView = launcher.getOverviewPanel();
         recentsView.onBeginTransitionToOverview();
+        // Request orientation be set to unspecified, letting the system decide the best
+        // orientation.
+        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index d1d697c..c228bb9 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -131,10 +131,11 @@
             return anim;
         }
 
-        View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+        View thumbnailView = mRecentsView.getBottomThumbnailView();
         if (thumbnailView == null) {
-            // TODO: We should either 1) guarantee the view is loaded before attempting this
-            // or 2) have a backup animation.
+            // This can be null if there were previously 0 tasks and the recycler view has not had
+            // enough time to take in the data change, bind a new view, and lay out the new view.
+            // TODO: Have a fallback to animate to
             if (Log.isLoggable(TAG, Log.WARN)) {
                 Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
             }
diff --git a/go/quickstep/src/com/android/quickstep/ClearAllHolder.java b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
new file mode 100644
index 0000000..ce87171
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Holder for clear all button view in task recycler view.
+ */
+final class ClearAllHolder extends ViewHolder {
+    public ClearAllHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java
new file mode 100644
index 0000000..1b6f2e3
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.view.View.ALPHA;
+
+import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
+import static com.android.quickstep.views.TaskItemView.CONTENT_TRANSITION_PROGRESS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.android.quickstep.views.TaskItemView;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An item animator that is only set and used for the transition from the empty loading UI to
+ * the filled task content UI. The animation starts from the bottom to top, changing all valid
+ * empty item views to be filled and removing all extra empty views.
+ */
+public final class ContentFillItemAnimator extends SimpleItemAnimator {
+
+    private static final class PendingAnimation {
+        ViewHolder viewHolder;
+        int animType;
+
+        PendingAnimation(ViewHolder vh, int type) {
+            viewHolder = vh;
+            animType = type;
+        }
+    }
+
+    private static final int ANIM_TYPE_REMOVE = 0;
+    private static final int ANIM_TYPE_CHANGE = 1;
+
+    private static final int ITEM_BETWEEN_DELAY = 40;
+    private static final int ITEM_CHANGE_DURATION = 150;
+    private static final int ITEM_REMOVE_DURATION = 150;
+
+    /**
+     * Animations that have been registered to occur together at the next call of
+     * {@link #runPendingAnimations()} but have not started.
+     */
+    private final ArrayList<PendingAnimation> mPendingAnims = new ArrayList<>();
+
+    /**
+     * Animations that have started and are running.
+     */
+    private final ArrayList<ObjectAnimator> mRunningAnims = new ArrayList<>();
+
+    private Runnable mOnFinishRunnable;
+
+    /**
+     * Set runnable to run after the content fill animation is fully completed.
+     *
+     * @param runnable runnable to run on end
+     */
+    public void setOnAnimationFinishedRunnable(Runnable runnable) {
+        mOnFinishRunnable = runnable;
+    }
+
+    @Override
+    public void setChangeDuration(long changeDuration) {
+        throw new UnsupportedOperationException("Cascading item animator cannot have animation "
+                + "duration changed.");
+    }
+
+    @Override
+    public void setRemoveDuration(long removeDuration) {
+        throw new UnsupportedOperationException("Cascading item animator cannot have animation "
+                + "duration changed.");
+    }
+
+    @Override
+    public boolean animateRemove(ViewHolder holder) {
+        PendingAnimation pendAnim = new PendingAnimation(holder, ANIM_TYPE_REMOVE);
+        mPendingAnims.add(pendAnim);
+        return true;
+    }
+
+    private void animateRemoveImpl(ViewHolder holder, long startDelay) {
+        final View view = holder.itemView;
+        if (holder.itemView.getAlpha() == 0) {
+            // View is already visually removed. We can just get rid of it now.
+            view.setAlpha(1.0f);
+            dispatchRemoveFinished(holder);
+            dispatchFinishedWhenDone();
+            return;
+        }
+        final ObjectAnimator anim = ObjectAnimator.ofFloat(
+                holder.itemView, ALPHA, holder.itemView.getAlpha(), 0.0f);
+        anim.setDuration(ITEM_REMOVE_DURATION).setStartDelay(startDelay);
+        anim.addListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        dispatchRemoveStarting(holder);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setAlpha(1);
+                        dispatchRemoveFinished(holder);
+                        mRunningAnims.remove(anim);
+                        dispatchFinishedWhenDone();
+                    }
+                }
+        );
+        anim.start();
+        mRunningAnims.add(anim);
+    }
+
+    @Override
+    public boolean animateAdd(ViewHolder holder) {
+        dispatchAddFinished(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateMove(ViewHolder holder, int fromX, int fromY, int toX,
+            int toY) {
+        dispatchMoveFinished(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateChange(ViewHolder oldHolder,
+            ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
+        // Only support changes where the holders are the same
+        if (oldHolder == newHolder) {
+            PendingAnimation pendAnim = new PendingAnimation(oldHolder, ANIM_TYPE_CHANGE);
+            mPendingAnims.add(pendAnim);
+            return true;
+        }
+        dispatchChangeFinished(oldHolder, true /* oldItem */);
+        dispatchChangeFinished(newHolder, false /* oldItem */);
+        return false;
+    }
+
+    private void animateChangeImpl(ViewHolder viewHolder, long startDelay) {
+        TaskItemView itemView = (TaskItemView) viewHolder.itemView;
+        final ObjectAnimator anim =
+                ObjectAnimator.ofFloat(itemView, CONTENT_TRANSITION_PROGRESS, 0.0f, 1.0f);
+        anim.setDuration(ITEM_CHANGE_DURATION).setStartDelay(startDelay);
+        anim.addListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        dispatchChangeStarting(viewHolder, true /* oldItem */);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        dispatchChangeFinished(viewHolder, true /* oldItem */);
+                        mRunningAnims.remove(anim);
+                        dispatchFinishedWhenDone();
+                    }
+                }
+        );
+        anim.start();
+        mRunningAnims.add(anim);
+    }
+
+    @Override
+    public void runPendingAnimations() {
+        // Run animations bottom to top.
+        mPendingAnims.sort(Comparator.comparingInt(o -> -o.viewHolder.itemView.getBottom()));
+        int delay = 0;
+        while (!mPendingAnims.isEmpty()) {
+            PendingAnimation curAnim = mPendingAnims.remove(0);
+            ViewHolder vh = curAnim.viewHolder;
+            switch (curAnim.animType) {
+                case ANIM_TYPE_REMOVE:
+                    animateRemoveImpl(vh, delay);
+                    break;
+                case ANIM_TYPE_CHANGE:
+                    animateChangeImpl(vh, delay);
+                    break;
+                default:
+                    break;
+            }
+            delay += ITEM_BETWEEN_DELAY;
+        }
+    }
+
+    @Override
+    public void endAnimation(@NonNull ViewHolder item) {
+        for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
+            PendingAnimation pendAnim = mPendingAnims.get(i);
+            if (pendAnim.viewHolder == item) {
+                mPendingAnims.remove(i);
+                switch (pendAnim.animType) {
+                    case ANIM_TYPE_REMOVE:
+                        dispatchRemoveFinished(item);
+                        break;
+                    case ANIM_TYPE_CHANGE:
+                        dispatchChangeFinished(item, true /* oldItem */);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+        dispatchFinishedWhenDone();
+    }
+
+    @Override
+    public void endAnimations() {
+        for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
+            PendingAnimation pendAnim = mPendingAnims.get(i);
+            ViewHolder item = pendAnim.viewHolder;
+            switch (pendAnim.animType) {
+                case ANIM_TYPE_REMOVE:
+                    dispatchRemoveFinished(item);
+                    break;
+                case ANIM_TYPE_CHANGE:
+                    dispatchChangeFinished(item, true /* oldItem */);
+                    break;
+                default:
+                    break;
+            }
+            mPendingAnims.remove(i);
+        }
+        for (int i = 0; i < mRunningAnims.size(); i++) {
+            ObjectAnimator anim = mRunningAnims.get(i);
+            anim.end();
+        }
+        dispatchAnimationsFinished();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return !mPendingAnims.isEmpty() || !mRunningAnims.isEmpty();
+    }
+
+    @Override
+    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
+            @NonNull List<Object> payloads) {
+        if (!payloads.isEmpty()
+                && (int) payloads.get(0) == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
+            return true;
+        }
+        return super.canReuseUpdatedViewHolder(viewHolder, payloads);
+    }
+
+    private void dispatchFinishedWhenDone() {
+        if (!isRunning()) {
+            dispatchAnimationsFinished();
+            if (mOnFinishRunnable != null) {
+                mOnFinishRunnable.run();
+            }
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskActionController.java b/go/quickstep/src/com/android/quickstep/TaskActionController.java
index 71bee91..0e921c0 100644
--- a/go/quickstep/src/com/android/quickstep/TaskActionController.java
+++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java
@@ -15,9 +15,13 @@
  */
 package com.android.quickstep;
 
+import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
+
 import android.app.ActivityOptions;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+
 import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -37,12 +41,12 @@
     }
 
     /**
-     * Launch the task associated with the task holder, animating into the app.
+     * Launch the task associated with the task holder, animating into the app from the task view.
      *
      * @param viewHolder the task view holder to launch
      */
-    public void launchTask(TaskHolder viewHolder) {
-        if (viewHolder.getTask() == null) {
+    public void launchTaskFromView(@NonNull TaskHolder viewHolder) {
+        if (!viewHolder.getTask().isPresent()) {
             return;
         }
         TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
@@ -53,8 +57,20 @@
         int height = v.getMeasuredHeight();
 
         ActivityOptions opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(viewHolder.getTask().key,
-                opts, null /* resultCallback */, null /* resultCallbackHandler */);
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
+                viewHolder.getTask().get().key, opts, null /* resultCallback */,
+                null /* resultCallbackHandler */);
+    }
+
+    /**
+     * Launch the task directly with a basic animation.
+     *
+     * @param task the task to launch
+     */
+    public void launchTask(@NonNull Task task) {
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
+                null /* resultCallback */, null /* resultCallbackHandler */);
     }
 
     /**
@@ -63,11 +79,11 @@
      * @param viewHolder the task view holder to remove
      */
     public void removeTask(TaskHolder viewHolder) {
-        if (viewHolder.getTask() == null) {
+        if (!viewHolder.getTask().isPresent()) {
             return;
         }
         int position = viewHolder.getAdapterPosition();
-        Task task = viewHolder.getTask();
+        Task task = viewHolder.getTask().get();
         ActivityManagerWrapper.getInstance().removeTask(task.key.id);
         mLoader.removeTask(task);
         mAdapter.notifyItemRemoved(position);
@@ -80,6 +96,6 @@
         int count = mAdapter.getItemCount();
         ActivityManagerWrapper.getInstance().removeAllRecentTasks();
         mLoader.clearAllTasks();
-        mAdapter.notifyItemRangeRemoved(0 /* positionStart */, count);
+        mAdapter.notifyItemRangeRemoved(TASKS_START_POSITION /* positionStart */, count);
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 674fcae..509bf29 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,13 +15,15 @@
  */
 package com.android.quickstep;
 
-import android.util.ArrayMap;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.widget.Button;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import com.android.launcher3.R;
 import com.android.quickstep.views.TaskItemView;
@@ -29,18 +31,25 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
  * appropriate {@link Task} from the recents task list.
  */
-public final class TaskAdapter extends Adapter<TaskHolder> {
+public final class TaskAdapter extends Adapter<ViewHolder> {
 
-    private static final int MAX_TASKS_TO_DISPLAY = 6;
+    public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0;
+    public static final int MAX_TASKS_TO_DISPLAY = 6;
+    public static final int TASKS_START_POSITION = 1;
+
+    public static final int ITEM_TYPE_TASK = 0;
+    public static final int ITEM_TYPE_CLEAR_ALL = 1;
+
     private static final String TAG = "TaskAdapter";
     private final TaskListLoader mLoader;
-    private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
     private TaskActionController mTaskActionController;
+    private OnClickListener mClearAllListener;
     private boolean mIsShowingLoadingUi;
 
     public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -51,6 +60,10 @@
         mTaskActionController = taskActionController;
     }
 
+    public void setOnClearAllClickListener(OnClickListener listener) {
+        mClearAllListener = listener;
+    }
+
     /**
      * Sets all positions in the task adapter to loading views, binding new views if necessary.
      * This changes the task adapter's view of the data, so the appropriate notify events should be
@@ -63,75 +76,104 @@
         mIsShowingLoadingUi = isShowingLoadingUi;
     }
 
-    /**
-     * Get task item view for a given task id if it's attached to the view.
-     *
-     * @param taskId task id to search for
-     * @return corresponding task item view if it's attached, null otherwise
-     */
-    public @Nullable TaskItemView getTaskItemView(int taskId) {
-        return mTaskIdToViewMap.get(taskId);
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case ITEM_TYPE_TASK:
+                TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.task_item_view, parent, false);
+                TaskHolder taskHolder = new TaskHolder(itemView);
+                itemView.setOnClickListener(
+                        view -> mTaskActionController.launchTaskFromView(taskHolder));
+                return taskHolder;
+            case ITEM_TYPE_CLEAR_ALL:
+                View clearView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.clear_all_button, parent, false);
+                ClearAllHolder clearAllHolder = new ClearAllHolder(clearView);
+                Button clearViewButton = clearView.findViewById(R.id.clear_all_button);
+                clearViewButton.setOnClickListener(mClearAllListener);
+                return clearAllHolder;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + viewType);
+        }
     }
 
     @Override
-    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.task_item_view, parent, false);
-        TaskHolder holder = new TaskHolder(itemView);
-        itemView.setOnClickListener(view -> mTaskActionController.launchTask(holder));
-        return holder;
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        onBindViewHolderInternal(holder, position, false /* willAnimate */);
     }
 
     @Override
-    public void onBindViewHolder(TaskHolder holder, int position) {
-        if (mIsShowingLoadingUi) {
-            holder.bindEmptyUi();
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
+            @NonNull List<Object> payloads) {
+        if (payloads.isEmpty()) {
+            super.onBindViewHolder(holder, position, payloads);
             return;
         }
-        List<Task> tasks = mLoader.getCurrentTaskList();
-        if (position >= tasks.size()) {
-            // Task list has updated.
-            return;
+        int changeType = (int) payloads.get(0);
+        if (changeType == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
+            // Bind in preparation for animation
+            onBindViewHolderInternal(holder, position, true /* willAnimate */);
+        } else {
+            throw new IllegalArgumentException("Payload content is not a valid change event type: "
+                    + changeType);
         }
-        Task task = tasks.get(position);
-        holder.bindTask(task);
-        mLoader.loadTaskIconAndLabel(task, () -> {
-            // Ensure holder still has the same task.
-            if (Objects.equals(task, holder.getTask())) {
-                holder.getTaskItemView().setIcon(task.icon);
-                holder.getTaskItemView().setLabel(task.titleDescription);
-            }
-        });
-        mLoader.loadTaskThumbnail(task, () -> {
-            if (Objects.equals(task, holder.getTask())) {
-                holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
-            }
-        });
+    }
+
+    private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position,
+            boolean willAnimate) {
+        int itemType = getItemViewType(position);
+        switch (itemType) {
+            case ITEM_TYPE_TASK:
+                TaskHolder taskHolder = (TaskHolder) holder;
+                if (mIsShowingLoadingUi) {
+                    taskHolder.bindEmptyUi();
+                    return;
+                }
+                List<Task> tasks = mLoader.getCurrentTaskList();
+                int taskPos = position - TASKS_START_POSITION;
+                if (taskPos >= tasks.size()) {
+                    // Task list has updated.
+                    return;
+                }
+                Task task = tasks.get(taskPos);
+                taskHolder.bindTask(task, willAnimate /* willAnimate */);
+                mLoader.loadTaskIconAndLabel(task, () -> {
+                    // Ensure holder still has the same task.
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setIcon(task.icon);
+                        taskHolder.getTaskItemView().setLabel(task.titleDescription);
+                    }
+                });
+                mLoader.loadTaskThumbnail(task, () -> {
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setThumbnail(task.thumbnail);
+                    }
+                });
+                break;
+            case ITEM_TYPE_CLEAR_ALL:
+                // Nothing to bind.
+                break;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + itemType);
+        }
     }
 
     @Override
-    public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
-        if (holder.getTask() == null) {
-            return;
-        }
-        mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
-        if (holder.getTask() == null) {
-            return;
-        }
-        mTaskIdToViewMap.remove(holder.getTask().key.id);
+    public int getItemViewType(int position) {
+        // Bottom is always clear all button.
+        return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK;
     }
 
     @Override
     public int getItemCount() {
+        int itemCount = TASKS_START_POSITION;
         if (mIsShowingLoadingUi) {
             // Show loading version of all items.
-            return MAX_TASKS_TO_DISPLAY;
+            itemCount += MAX_TASKS_TO_DISPLAY;
         } else {
-            return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
+            itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
         }
+        return itemCount;
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 98dc989..49b6aaa 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,12 +15,14 @@
  */
 package com.android.quickstep;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
 
+import java.util.Optional;
+
 /**
  * A recycler view holder that holds the task view and binds {@link Task} content (app title, icon,
  * etc.) to the view.
@@ -40,13 +42,27 @@
     }
 
     /**
-     * Bind a task to the holder, resetting the view and preparing it for content to load in.
+     * Bind the task model to the holder. This will take the current task content in the task
+     * object (i.e. icon, thumbnail, label) and either apply the content immediately or simply bind
+     * the content to animate to at a later time. If the task does not have all its content loaded,
+     * the view will prepare appropriate default placeholders and it is the callers responsibility
+     * to change them at a later time.
+     *
+     * Regardless of whether it is animating, input handlers will be bound immediately (see
+     * {@link TaskActionController}).
      *
      * @param task the task to bind to the view
+     * @param willAnimate true if UI should animate in later, false if it should apply immediately
      */
-    public void bindTask(Task task) {
+    public void bindTask(@NonNull Task task, boolean willAnimate) {
         mTask = task;
-        mTaskItemView.resetTaskItemView();
+        if (willAnimate) {
+            mTaskItemView.startContentAnimation(task.icon, task.thumbnail, task.titleDescription);
+        } else {
+            mTaskItemView.setIcon(task.icon);
+            mTaskItemView.setThumbnail(task.thumbnail);
+            mTaskItemView.setLabel(task.titleDescription);
+        }
     }
 
     /**
@@ -55,10 +71,7 @@
      */
     public void bindEmptyUi() {
         mTask = null;
-        // TODO: Set the task view to a loading, empty UI.
-        // Temporarily using the one below for visual confirmation but should be swapped out to new
-        // UI later.
-        mTaskItemView.resetTaskItemView();
+        mTaskItemView.resetToEmptyUi();
     }
 
     /**
@@ -66,7 +79,7 @@
      *
      * @return the current task
      */
-    public @Nullable Task getTask() {
-        return mTask;
+    public Optional<Task> getTask() {
+        return Optional.ofNullable(mTask);
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index 51b73f1..850c7e6 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -38,23 +38,9 @@
 
     private ArrayList<Task> mTaskList = new ArrayList<>();
     private int mTaskListChangeId;
-    private RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
-        Task foundTask = null;
-        for (Task task : mTaskList) {
-            if (task.key.id == taskId) {
-                foundTask = task;
-                break;
-            }
-        }
-        if (foundTask != null) {
-            foundTask.thumbnail = thumbnailData;
-        }
-        return foundTask;
-    };
 
     public TaskListLoader(Context context) {
         mRecentsModel = RecentsModel.INSTANCE.get(context);
-        mRecentsModel.addThumbnailChangeListener(listener);
     }
 
     /**
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
index 98407d8..19951bb 100644
--- a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -17,6 +17,9 @@
 
 import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
 
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
+
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -45,4 +48,14 @@
             mTaskActionController.removeTask((TaskHolder) viewHolder);
         }
     }
+
+    @Override
+    public int getSwipeDirs(@NonNull RecyclerView recyclerView,
+            @NonNull ViewHolder viewHolder) {
+        if (viewHolder.getItemViewType() == ITEM_TYPE_CLEAR_ALL) {
+            // Clear all button should not be swipable.
+            return 0;
+        }
+        return super.getSwipeDirs(recyclerView, viewHolder);
+    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
new file mode 100644
index 0000000..6ef9039
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Bitmap backed drawable that supports rotating the thumbnail bitmap depending on if the
+ * orientation the thumbnail was taken in matches the desired orientation. In addition, the
+ * thumbnail always fills into the containing bounds.
+ */
+public final class ThumbnailDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint();
+    private final Matrix mMatrix = new Matrix();
+    private final ThumbnailData mThumbnailData;
+    private int mRequestedOrientation;
+
+    public ThumbnailDrawable(@NonNull ThumbnailData thumbnailData, int requestedOrientation) {
+        mThumbnailData = thumbnailData;
+        mRequestedOrientation = requestedOrientation;
+        updateMatrix();
+    }
+
+    /**
+     * Set the requested orientation.
+     *
+     * @param orientation the orientation we want the thumbnail to be in
+     */
+    public void setRequestedOrientation(int orientation) {
+        if (mRequestedOrientation != orientation) {
+            mRequestedOrientation = orientation;
+            updateMatrix();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        canvas.drawBitmap(mThumbnailData.thumbnail, mMatrix, mPaint);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        updateMatrix();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final int oldAlpha = mPaint.getAlpha();
+        if (alpha != oldAlpha) {
+            mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    private void updateMatrix() {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        mMatrix.reset();
+        float scaleX;
+        float scaleY;
+        Rect bounds = getBounds();
+        Bitmap thumbnail = mThumbnailData.thumbnail;
+        if (mRequestedOrientation != mThumbnailData.orientation) {
+            // Rotate and translate so that top left is the same.
+            mMatrix.postRotate(90, 0, 0);
+            mMatrix.postTranslate(thumbnail.getHeight(), 0);
+
+            scaleX = (float) bounds.width() / thumbnail.getHeight();
+            scaleY = (float) bounds.height() / thumbnail.getWidth();
+        } else {
+            scaleX = (float) bounds.width() / thumbnail.getWidth();
+            scaleY = (float) bounds.height() / thumbnail.getHeight();
+        }
+        // Scale to fill.
+        mMatrix.postScale(scaleX, scaleY);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java b/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java
new file mode 100644
index 0000000..378dbf4
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static com.android.quickstep.views.TaskLayoutUtils.getClearAllItemHeight;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Recycler view item that lays out the clear all button and measures the space it takes based on
+ * the device height.
+ */
+public final class ClearAllItemView extends FrameLayout {
+
+    public ClearAllItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int buttonHeight = getClearAllItemHeight(getContext());
+        int newHeightSpec = MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, newHeightSpec);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index c06b6ec..cf6eb6d 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -15,8 +15,13 @@
  */
 package com.android.quickstep.views;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
 import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
 
+import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
+import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -24,30 +29,39 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.LayoutAnimationController;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
 
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.R;
+import com.android.quickstep.ContentFillItemAnimator;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsToActivityHelper;
 import com.android.quickstep.TaskActionController;
 import com.android.quickstep.TaskAdapter;
 import com.android.quickstep.TaskHolder;
 import com.android.quickstep.TaskListLoader;
 import com.android.quickstep.TaskSwipeCallback;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -89,24 +103,48 @@
     private final Context mContext;
     private final TaskListLoader mTaskLoader;
     private final TaskAdapter mTaskAdapter;
+    private final LinearLayoutManager mTaskLayoutManager;
     private final TaskActionController mTaskActionController;
-    private final LayoutAnimationController mLayoutAnimation;
+    private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator();
+    private final ContentFillItemAnimator mLoadingContentItemAnimator =
+            new ContentFillItemAnimator();
 
     private RecentsToActivityHelper mActivityHelper;
     private RecyclerView mTaskRecyclerView;
+    private View mShowingContentView;
     private View mEmptyView;
     private View mContentView;
-    private View mClearAllView;
     private boolean mTransitionedFromApp;
+    private AnimatorSet mLayoutAnimation;
+    private final ArraySet<View> mLayingOutViews = new ArraySet<>();
+    private final RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
+        ArrayList<TaskItemView> itemViews = getTaskViews();
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView taskView = itemViews.get(i);
+            TaskHolder taskHolder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
+            Optional<Task> optTask = taskHolder.getTask();
+            if (optTask.filter(task -> task.key.id == taskId).isPresent()) {
+                Task task = optTask.get();
+                // Update thumbnail on the task.
+                task.thumbnail = thumbnailData;
+                taskView.setThumbnail(thumbnailData);
+                return task;
+            }
+        }
+        return null;
+    };
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        BaseActivity activity = BaseActivity.fromContext(context);
         mContext = context;
         mTaskLoader = new TaskListLoader(mContext);
         mTaskAdapter = new TaskAdapter(mTaskLoader);
+        mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
         mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
         mTaskAdapter.setActionController(mTaskActionController);
-        mLayoutAnimation = createLayoutAnimation();
+        mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
+        RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
     }
 
     @Override
@@ -115,15 +153,30 @@
         if (mTaskRecyclerView == null) {
             mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
             mTaskRecyclerView.setAdapter(mTaskAdapter);
-            mTaskRecyclerView.setLayoutManager(
-                    new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+            mTaskRecyclerView.setLayoutManager(mTaskLayoutManager);
             ItemTouchHelper helper = new ItemTouchHelper(
                     new TaskSwipeCallback(mTaskActionController));
             helper.attachToRecyclerView(mTaskRecyclerView);
-            mTaskRecyclerView.setLayoutAnimation(mLayoutAnimation);
+            mTaskRecyclerView.addOnChildAttachStateChangeListener(
+                    new OnChildAttachStateChangeListener() {
+                        @Override
+                        public void onChildViewAttachedToWindow(@NonNull View view) {
+                            if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) {
+                                // Child view was added that is not part of current layout animation
+                                // so restart the animation.
+                                animateFadeInLayoutAnimation();
+                            }
+                        }
+
+                        @Override
+                        public void onChildViewDetachedFromWindow(@NonNull View view) { }
+                    });
+            mTaskRecyclerView.setItemAnimator(mDefaultItemAnimator);
+            mLoadingContentItemAnimator.setOnAnimationFinishedRunnable(
+                    () -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));
 
             mEmptyView = findViewById(R.id.recent_task_empty_view);
-            mContentView = findViewById(R.id.recent_task_content_view);
+            mContentView = mTaskRecyclerView;
             mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
                 @Override
                 public void onChanged() {
@@ -135,19 +188,17 @@
                     updateContentViewVisibility();
                 }
             });
-            mClearAllView = findViewById(R.id.clear_all_button);
-            mClearAllView.setOnClickListener(v -> animateClearAllTasks());
+            // TODO: Move layout param logic into onMeasure
         }
     }
 
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
-        TaskItemView[] itemViews = getTaskViews();
-        for (TaskItemView itemView : itemViews) {
-            itemView.setEnabled(enabled);
+        int childCount = mTaskRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            mTaskRecyclerView.getChildAt(i).setEnabled(enabled);
         }
-        mClearAllView.setEnabled(enabled);
     }
 
     /**
@@ -165,8 +216,13 @@
      * becomes visible.
      */
     public void onBeginTransitionToOverview() {
-        mTaskRecyclerView.scheduleLayoutAnimation();
-
+        if (mContext.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+            // Scroll to bottom of task in landscape mode. This is a non-issue in portrait mode as
+            // all tasks should be visible to fill up the screen in portrait mode and the view will
+            // not be scrollable.
+            mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */);
+        }
+        scheduleFadeInLayoutAnimation();
         // Load any task changes
         if (!mTaskLoader.needsToLoad()) {
             return;
@@ -174,9 +230,24 @@
         mTaskAdapter.setIsShowingLoadingUi(true);
         mTaskAdapter.notifyDataSetChanged();
         mTaskLoader.loadTaskList(tasks -> {
+            int numEmptyItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
             mTaskAdapter.setIsShowingLoadingUi(false);
-            // TODO: Animate the loading UI out and the loaded data in.
-            mTaskAdapter.notifyDataSetChanged();
+            int numActualItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
+            if (numEmptyItems < numActualItems) {
+                throw new IllegalStateException("There are less empty item views than the number "
+                        + "of items to animate to.");
+            }
+            // Possible that task list loads faster than adapter changes propagate to layout so
+            // only start content fill animation if there aren't any pending adapter changes.
+            if (!mTaskRecyclerView.hasPendingAdapterUpdates()) {
+                // Set item animator for content filling animation. The item animator will switch
+                // back to the default on completion
+                mTaskRecyclerView.setItemAnimator(mLoadingContentItemAnimator);
+            }
+            mTaskAdapter.notifyItemRangeRemoved(TASKS_START_POSITION + numActualItems,
+                    numEmptyItems - numActualItems);
+            mTaskAdapter.notifyItemRangeChanged(TASKS_START_POSITION, numActualItems,
+                    CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT);
         });
     }
 
@@ -194,35 +265,47 @@
      * the app. In that case, we launch the next most recent.
      */
     public void handleOverviewCommand() {
-        int childCount = mTaskRecyclerView.getChildCount();
-        if (childCount == 0) {
+        List<Task> tasks = mTaskLoader.getCurrentTaskList();
+        int tasksSize = tasks.size();
+        if (tasksSize == 0) {
             // Do nothing
             return;
         }
-        TaskHolder taskToLaunch;
-        if (mTransitionedFromApp && childCount > 1) {
+        Task taskToLaunch;
+        if (mTransitionedFromApp && tasksSize > 1) {
             // Launch the next most recent app
-            TaskItemView itemView = (TaskItemView) mTaskRecyclerView.getChildAt(1);
-            taskToLaunch = (TaskHolder) mTaskRecyclerView.getChildViewHolder(itemView);
+            taskToLaunch = tasks.get(1);
         } else {
             // Launch the most recent app
-            TaskItemView itemView = (TaskItemView) mTaskRecyclerView.getChildAt(0);
-            taskToLaunch = (TaskHolder) mTaskRecyclerView.getChildViewHolder(itemView);
+            taskToLaunch = tasks.get(0);
         }
+
+        // See if view for this task is attached, and if so, animate launch from that view.
+        ArrayList<TaskItemView> itemViews = getTaskViews();
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView taskView = itemViews.get(i);
+            TaskHolder holder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
+            if (Objects.equals(holder.getTask(), Optional.of(taskToLaunch))) {
+                mTaskActionController.launchTaskFromView(holder);
+                return;
+            }
+        }
+
+        // Otherwise, just use a basic launch animation.
         mTaskActionController.launchTask(taskToLaunch);
     }
 
     /**
-     * Get the thumbnail view associated with a task for the purposes of animation.
+     * Get the bottom most thumbnail view to animate to.
      *
-     * @param taskId task id of thumbnail view to get
-     * @return the thumbnail view for the task if attached, null otherwise
+     * @return the thumbnail view if laid out
      */
-    public @Nullable View getThumbnailViewForTask(int taskId) {
-        TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
-        if (view == null) {
+    public @Nullable View getBottomThumbnailView() {
+        ArrayList<TaskItemView> taskViews = getTaskViews();
+        if (taskViews.isEmpty()) {
             return null;
         }
+        TaskItemView view = taskViews.get(0);
         return view.getThumbnailView();
     }
 
@@ -231,13 +314,14 @@
      */
     private void animateClearAllTasks() {
         setEnabled(false);
-        TaskItemView[] itemViews = getTaskViews();
+        ArrayList<TaskItemView> itemViews = getTaskViews();
 
         AnimatorSet clearAnim = new AnimatorSet();
         long currentDelay = 0;
 
         // Animate each item view to the right and fade out.
-        for (TaskItemView itemView : itemViews) {
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView itemView = itemViews.get(i);
             PropertyValuesHolder transXproperty = PropertyValuesHolder.ofFloat(TRANSLATION_X,
                     0, itemView.getWidth() * ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO);
             PropertyValuesHolder alphaProperty = PropertyValuesHolder.ofFloat(ALPHA, 1.0f, 0f);
@@ -272,7 +356,8 @@
         clearAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                for (TaskItemView itemView : itemViews) {
+                for (int i = 0, size = itemViews.size(); i < size; i++) {
+                    TaskItemView itemView = itemViews.get(i);
                     itemView.setTranslationX(0);
                     itemView.setAlpha(1.0f);
                 }
@@ -287,13 +372,16 @@
     /**
      * Get attached task item views ordered by most recent.
      *
-     * @return array of attached task item views
+     * @return array list of attached task item views
      */
-    private TaskItemView[] getTaskViews() {
+    private ArrayList<TaskItemView> getTaskViews() {
         int taskCount = mTaskRecyclerView.getChildCount();
-        TaskItemView[] itemViews = new TaskItemView[taskCount];
+        ArrayList<TaskItemView> itemViews = new ArrayList<>();
         for (int i = 0; i < taskCount; i ++) {
-            itemViews[i] = (TaskItemView) mTaskRecyclerView.getChildAt(i);
+            View child = mTaskRecyclerView.getChildAt(i);
+            if (child instanceof TaskItemView) {
+                itemViews.add((TaskItemView) child);
+            }
         }
         return itemViews;
     }
@@ -303,12 +391,14 @@
      * of tasks.
      */
     private void updateContentViewVisibility() {
-        int taskListSize = mTaskLoader.getCurrentTaskList().size();
-        if (mEmptyView.getVisibility() != VISIBLE && taskListSize == 0) {
+        int taskListSize = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
+        if (mShowingContentView != mEmptyView && taskListSize == 0) {
+            mShowingContentView = mEmptyView;
             crossfadeViews(mEmptyView, mContentView);
             mActivityHelper.leaveRecents();
         }
-        if (mContentView.getVisibility() != VISIBLE && taskListSize > 0) {
+        if (mShowingContentView != mContentView && taskListSize > 0) {
+            mShowingContentView = mContentView;
             crossfadeViews(mContentView, mEmptyView);
         }
     }
@@ -320,6 +410,7 @@
      * @param fadeOutView view that should fade out
      */
     private void crossfadeViews(View fadeInView, View fadeOutView) {
+        fadeInView.animate().cancel();
         fadeInView.setVisibility(VISIBLE);
         fadeInView.setAlpha(0f);
         fadeInView.animate()
@@ -327,6 +418,7 @@
                 .setDuration(CROSSFADE_DURATION)
                 .setListener(null);
 
+        fadeOutView.animate().cancel();
         fadeOutView.animate()
                 .alpha(0f)
                 .setDuration(CROSSFADE_DURATION)
@@ -338,17 +430,56 @@
                 });
     }
 
-    private static LayoutAnimationController createLayoutAnimation() {
-        AnimationSet anim = new AnimationSet(false /* shareInterpolator */);
+    /**
+     * Schedule a one-shot layout animation on the next layout. Separate from
+     * {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the
+     * view properties themselves, allowing more controllable behavior and making it easier to
+     * manage when the animation conflicts with another animation.
+     */
+    private void scheduleFadeInLayoutAnimation() {
+        mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        animateFadeInLayoutAnimation();
+                        mTaskRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    }
+                });
+    }
 
-        Animation alphaAnim = new AlphaAnimation(0, 1);
-        alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION);
-        anim.addAnimation(alphaAnim);
-
-        LayoutAnimationController layoutAnim = new LayoutAnimationController(anim);
-        layoutAnim.setDelay(
-                (float) LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN / LAYOUT_ITEM_ANIMATE_IN_DURATION);
-
-        return layoutAnim;
+    /**
+     * Start animating the layout animation where items fade in.
+     */
+    private void animateFadeInLayoutAnimation() {
+        if (mLayoutAnimation != null) {
+            // If layout animation still in progress, cancel and restart.
+            mLayoutAnimation.cancel();
+        }
+        ArrayList<TaskItemView> views = getTaskViews();
+        int delay = 0;
+        mLayoutAnimation = new AnimatorSet();
+        for (int i = 0, size = views.size(); i < size; i++) {
+            TaskItemView view = views.get(i);
+            view.setAlpha(0.0f);
+            Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f);
+            alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay);
+            alphaAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    view.setAlpha(1.0f);
+                    mLayingOutViews.remove(view);
+                }
+            });
+            delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN;
+            mLayoutAnimation.play(alphaAnim);
+            mLayingOutViews.add(view);
+        }
+        mLayoutAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mLayoutAnimation = null;
+            }
+        });
+        mLayoutAnimation.start();
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index d831b20..0b5ed56 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -15,35 +15,66 @@
  */
 package com.android.quickstep.views;
 
+import static com.android.quickstep.views.TaskLayoutUtils.getTaskHeight;
+
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.quickstep.ThumbnailDrawable;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 
 /**
  * View representing an individual task item with the icon + thumbnail adjacent to the task label.
  */
 public final class TaskItemView extends LinearLayout {
 
+    private static final String EMPTY_LABEL = "";
     private static final String DEFAULT_LABEL = "...";
     private final Drawable mDefaultIcon;
+    private final Drawable mDefaultThumbnail;
+    private final TaskLayerDrawable mIconDrawable;
+    private final TaskLayerDrawable mThumbnailDrawable;
     private TextView mLabelView;
     private ImageView mIconView;
     private ImageView mThumbnailView;
+    private float mContentTransitionProgress;
+
+    /**
+     * Property representing the content transition progress of the view. 1.0f represents that the
+     * currently bound icon, thumbnail, and label are fully animated in and visible.
+     */
+    public static FloatProperty CONTENT_TRANSITION_PROGRESS =
+            new FloatProperty<TaskItemView>("taskContentTransitionProgress") {
+                @Override
+                public void setValue(TaskItemView view, float progress) {
+                    view.setContentTransitionProgress(progress);
+                }
+
+                @Override
+                public Float get(TaskItemView view) {
+                    return view.mContentTransitionProgress;
+                }
+            };
 
     public TaskItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mDefaultIcon = context.getResources().getDrawable(
-                android.R.drawable.sym_def_app_icon, context.getTheme());
+        Resources res = context.getResources();
+        mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme());
+        mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme());
+        mIconDrawable = new TaskLayerDrawable(context);
+        mThumbnailDrawable = new TaskLayerDrawable(context);
     }
 
     @Override
@@ -52,15 +83,28 @@
         mLabelView = findViewById(R.id.task_label);
         mThumbnailView = findViewById(R.id.task_thumbnail);
         mIconView = findViewById(R.id.task_icon);
+
+        mThumbnailView.setImageDrawable(mThumbnailDrawable);
+        mIconView.setImageDrawable(mIconDrawable);
+
+        resetToEmptyUi();
+        CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int taskHeight = getTaskHeight(getContext());
+        int newHeightSpec = MeasureSpec.makeMeasureSpec(taskHeight,MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, newHeightSpec);
     }
 
     /**
-     * Resets task item view to default values.
+     * Resets task item view to empty, loading UI.
      */
-    public void resetTaskItemView() {
-        setLabel(DEFAULT_LABEL);
-        setIcon(null);
-        setThumbnail(null);
+    public void resetToEmptyUi() {
+        mIconDrawable.resetDrawable();
+        mThumbnailDrawable.resetDrawable();
+        setLabel(EMPTY_LABEL);
     }
 
     /**
@@ -69,11 +113,8 @@
      * @param label task label
      */
     public void setLabel(@Nullable String label) {
-        if (label == null) {
-            mLabelView.setText(DEFAULT_LABEL);
-            return;
-        }
-        mLabelView.setText(label);
+        mLabelView.setText(getSafeLabel(label));
+        // TODO: Animation for label
     }
 
     /**
@@ -86,29 +127,72 @@
         // The icon proper is actually smaller than the drawable and has "padding" on the side for
         // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
         // view if we want the icon to be flush with the bottom of the thumbnail.
-        if (icon == null) {
-            mIconView.setImageDrawable(mDefaultIcon);
-            return;
-        }
-        mIconView.setImageDrawable(icon);
+        mIconDrawable.setCurrentDrawable(getSafeIcon(icon));
     }
 
     /**
      * Set the task thumbnail for the task. Sets to a default thumbnail if null.
      *
-     * @param thumbnail task thumbnail for the task
+     * @param thumbnailData task thumbnail data for the task
      */
-    public void setThumbnail(@Nullable Bitmap thumbnail) {
-        if (thumbnail == null) {
-            mThumbnailView.setImageBitmap(null);
-            mThumbnailView.setBackgroundColor(Color.GRAY);
-            return;
-        }
-        mThumbnailView.setBackgroundColor(Color.TRANSPARENT);
-        mThumbnailView.setImageBitmap(thumbnail);
+    public void setThumbnail(@Nullable ThumbnailData thumbnailData) {
+        mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnailData));
     }
 
     public View getThumbnailView() {
         return mThumbnailView;
     }
+
+    /**
+     * Start a new animation from the current task content to the specified new content. The caller
+     * is responsible for the actual animation control via the property
+     * {@link #CONTENT_TRANSITION_PROGRESS}.
+     *
+     * @param endIcon the icon to animate to
+     * @param endThumbnail the thumbnail to animate to
+     * @param endLabel the label to animate to
+     */
+    public void startContentAnimation(@Nullable Drawable endIcon,
+            @Nullable ThumbnailData endThumbnail, @Nullable String endLabel) {
+        mIconDrawable.startNewTransition(getSafeIcon(endIcon));
+        mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
+        // TODO: Animation for label
+
+        setContentTransitionProgress(0.0f);
+    }
+
+    private void setContentTransitionProgress(float progress) {
+        mContentTransitionProgress = progress;
+        mIconDrawable.setTransitionProgress(progress);
+        mThumbnailDrawable.setTransitionProgress(progress);
+        // TODO: Animation for label
+    }
+
+    private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) {
+        return (icon != null) ? icon : mDefaultIcon;
+    }
+
+    private @NonNull Drawable getSafeThumbnail(@Nullable ThumbnailData thumbnailData) {
+        if (thumbnailData == null || thumbnailData.thumbnail == null) {
+            return mDefaultThumbnail;
+        }
+        int orientation = getResources().getConfiguration().orientation;
+        return new ThumbnailDrawable(thumbnailData,  orientation /* requestedOrientation */);
+    }
+
+    private @NonNull String getSafeLabel(@Nullable String label) {
+        return (label != null) ? label : DEFAULT_LABEL;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        int layerCount = mThumbnailDrawable.getNumberOfLayers();
+        for (int i = 0; i < layerCount; i++) {
+            Drawable drawable = mThumbnailDrawable.getDrawable(i);
+            if (drawable instanceof ThumbnailDrawable) {
+                ((ThumbnailDrawable) drawable).setRequestedOrientation(newConfig.orientation);
+            }
+        }
+    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
new file mode 100644
index 0000000..98b66b9
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+
+/**
+ * A layer drawable for task content that transitions between two drawables by crossfading. Similar
+ * to {@link android.graphics.drawable.TransitionDrawable} but allows callers to control transition
+ * progress and provides a default, empty drawable.
+ */
+public final class TaskLayerDrawable extends LayerDrawable {
+    private final Drawable mEmptyDrawable;
+    private float mProgress;
+
+    public TaskLayerDrawable(Context context) {
+        super(new Drawable[0]);
+
+        // Use empty drawable for both layers initially.
+        mEmptyDrawable = context.getResources().getDrawable(
+                R.drawable.empty_content_box, context.getTheme());
+        addLayer(mEmptyDrawable);
+        addLayer(mEmptyDrawable);
+        setTransitionProgress(1.0f);
+    }
+
+    /**
+     * Immediately set the front-most drawable layer.
+     *
+     * @param drawable drawable to set
+     */
+    public void setCurrentDrawable(@NonNull Drawable drawable) {
+        setDrawable(0, drawable);
+        applyTransitionProgress(mProgress);
+    }
+
+    /**
+     * Immediately reset the drawable to showing the empty drawable.
+     */
+    public void resetDrawable() {
+        setCurrentDrawable(mEmptyDrawable);
+    }
+
+    /**
+     * Prepare to start animating the transition by pushing the current drawable to the back and
+     * setting a new drawable to the front layer and making it invisible.
+     *
+     * @param endDrawable drawable to animate to
+     */
+    public void startNewTransition(@NonNull Drawable endDrawable) {
+        Drawable oldDrawable = getDrawable(0);
+        setDrawable(1, oldDrawable);
+        setDrawable(0, endDrawable);
+        setTransitionProgress(0.0f);
+    }
+
+    /**
+     * Set the progress of the transition animation to crossfade the two drawables.
+     *
+     * @param progress current transition progress between 0 (front view invisible) and 1
+     *                 (front view visible)
+     */
+    public void setTransitionProgress(float progress) {
+        if (progress > 1 || progress < 0) {
+            throw new IllegalArgumentException("Transition progress should be between 0 and 1");
+        }
+        mProgress = progress;
+        applyTransitionProgress(progress);
+    }
+
+    private void applyTransitionProgress(float progress) {
+        int drawableAlpha = (int) (progress * 255);
+        getDrawable(0).setAlpha(drawableAlpha);
+        if (getDrawable(0) != getDrawable(1)) {
+            // Only do this if it's a different drawable so that it fades out.
+            // Otherwise, we'd just be overwriting the front drawable's alpha.
+            getDrawable(1).setAlpha(255 - drawableAlpha);
+        }
+        invalidateSelf();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java b/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
new file mode 100644
index 0000000..e28a9e0
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY;
+
+import android.content.Context;
+
+import com.android.launcher3.InvariantDeviceProfile;
+
+/**
+ * Utils to determine dynamically task and view sizes based off the device height and width.
+ */
+public final class TaskLayoutUtils {
+
+    private static final float CLEAR_ALL_ITEM_TO_HEIGHT_RATIO = 7.0f / 64;
+
+    private TaskLayoutUtils() {}
+
+    /**
+     * Calculate task height based off the available height in portrait mode such that when the
+     * recents list is full, the total height fills in the available device height perfectly. In
+     * landscape mode, we keep the same task height so that tasks scroll off the top.
+     *
+     * @param context current context
+     * @return task height
+     */
+    public static int getTaskHeight(Context context) {
+        final int availableHeight =
+                InvariantDeviceProfile.INSTANCE.get(context).portraitProfile.availableHeightPx;
+        final int availableTaskSpace = availableHeight - getClearAllItemHeight(context);
+        return (int) (availableTaskSpace * 1.0f / MAX_TASKS_TO_DISPLAY);
+    }
+
+    /**
+     * Calculate clear all item height scaled to available height in portrait mode.
+     *
+     * @param context current context
+     * @return clear all item height
+     */
+    public static int getClearAllItemHeight(Context context) {
+        final int availableHeight =
+                InvariantDeviceProfile.INSTANCE.get(context).portraitProfile.availableHeightPx;
+        return (int) (CLEAR_ALL_ITEM_TO_HEIGHT_RATIO * availableHeight);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
new file mode 100644
index 0000000..b1c60dd
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+
+/**
+ * Square view that holds thumbnail and icon and shrinks them appropriately so that both fit nicely
+ * within the view. Side length is determined by height.
+ */
+final class TaskThumbnailIconView extends ViewGroup {
+    private final Rect mTmpFrameRect = new Rect();
+    private final Rect mTmpChildRect = new Rect();
+    private View mThumbnailView;
+    private View mIconView;
+    private static final float SUBITEM_FRAME_RATIO = .6f;
+
+    public TaskThumbnailIconView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mThumbnailView = findViewById(R.id.task_thumbnail);
+        mIconView = findViewById(R.id.task_icon);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
+        int width = height;
+        setMeasuredDimension(width, height);
+
+        int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
+        if (mThumbnailView.getVisibility() != GONE) {
+            int thumbnailHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            int thumbnailWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            measureChild(mThumbnailView, thumbnailWidthSpec, thumbnailHeightSpec);
+        }
+        if (mIconView.getVisibility() != GONE) {
+            int iconHeightSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            int iconWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            measureChild(mIconView, iconWidthSpec, iconHeightSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mTmpFrameRect.left = getPaddingLeft();
+        mTmpFrameRect.right = right - left - getPaddingRight();
+        mTmpFrameRect.top = getPaddingTop();
+        mTmpFrameRect.bottom = bottom - top - getPaddingBottom();
+
+        // Layout the thumbnail to the top-start corner of the view
+        if (mThumbnailView.getVisibility() != GONE) {
+            final int width = mThumbnailView.getMeasuredWidth();
+            final int height = mThumbnailView.getMeasuredHeight();
+
+            final int thumbnailGravity = Gravity.TOP | Gravity.START;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mThumbnailView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+
+        // Layout the icon to the bottom-end corner of the view
+        if (mIconView.getVisibility() != GONE) {
+            final int width = mIconView.getMeasuredWidth();
+            final int height = mIconView.getMeasuredHeight();
+
+            int thumbnailGravity = Gravity.BOTTOM | Gravity.END;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mIconView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+    }
+}
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index ff0c907..1e44910 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -55,7 +56,7 @@
         return false;
     }
 
-    public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
+    public void onShortcutsChanged(List<ShortcutInfo> shortcuts) {
     }
 
     /**
@@ -64,7 +65,7 @@
      * This method is intended to get the full details for shortcuts when they are added or updated,
      * because we only get "key" fields in onShortcutsChanged().
      */
-    public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+    public List<ShortcutInfo> queryForFullDetails(String packageName,
             List<String> shortcutIds, UserHandle user) {
         return Collections.emptyList();
     }
@@ -73,7 +74,7 @@
      * Gets all the manifest and dynamic shortcuts associated with the given package and user,
      * to be displayed in the shortcuts container on long press.
      */
-    public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
             UserHandle user) {
         return Collections.emptyList();
     }
@@ -96,7 +97,7 @@
             Bundle startActivityOptions, UserHandle user) {
     }
 
-    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+    public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
         return null;
     }
 
@@ -105,16 +106,16 @@
      *
      * If packageName is null, returns all pinned shortcuts regardless of package.
      */
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
         return Collections.emptyList();
     }
 
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
             List<String> shortcutIds, UserHandle user) {
         return Collections.emptyList();
     }
 
-    public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
+    public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
         return Collections.emptyList();
     }
 
diff --git a/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
new file mode 100644
index 0000000..52cc6fc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+    Copyright (C) 2018 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="8dp" />
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
new file mode 100644
index 0000000..b0f2b4b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingEnd="4dp"
+        android:background="@drawable/arrow_toast_rounded_background"
+        android:layout_gravity="center_horizontal"
+        android:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center_vertical"
+            android:textColor="@android:color/white"
+            android:textSize="16sp"/>
+        <ImageView
+            android:id="@+id/dismiss"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_gravity="center_vertical"
+            android:padding="10dp"
+            android:layout_marginStart="2dp"
+            android:layout_marginEnd="2dp"
+            android:alpha="0.7"
+            android:src="@drawable/ic_remove_no_shadow"
+            android:tint="@android:color/white"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:contentDescription="@string/accessibility_close_task"/>
+    </LinearLayout>
+
+    <View
+        android:id="@+id/arrow"
+        android:elevation="2dp"
+        android:layout_width="10dp"
+        android:layout_height="8dp"
+        android:layout_marginTop="-2dp"
+        android:layout_gravity="center_horizontal"/>
+</merge>
diff --git a/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
new file mode 100644
index 0000000..b21c34b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <com.android.launcher3.appprediction.PredictionRowView
+        android:id="@+id/prediction_row"
+        android:accessibilityPaneTitle="@string/title_app_suggestions"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <com.android.launcher3.appprediction.AppsDividerView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/apps_divider_view" />
+</merge>
diff --git a/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml b/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml
new file mode 100644
index 0000000..20c4004
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    style="?android:attr/progressBarStyleHorizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:layout_marginLeft="20dp"
+    android:layout_marginRight="20dp"
+    android:indeterminate="true"
+    android:indeterminateOnly="true"
+    android:indeterminateTint="?workspaceTextColor" />
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
index 1e8d0cc..7426e30 100644
--- a/quickstep/recents_ui_overrides/res/values/colors.xml
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -1,4 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <color name="chip_hint_foreground_color">#fff</color>
+
+    <color name="all_apps_label_text">#61000000</color>
+    <color name="all_apps_label_text_dark">#61FFFFFF</color>
+    <color name="all_apps_prediction_row_separator">#3c000000</color>
+    <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index b654d5c..f991435 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -12,4 +12,13 @@
     <dimen name="chip_text_top_padding">4dp</dimen>
     <dimen name="chip_text_start_padding">10dp</dimen>
     <dimen name="chip_text_size">14sp</dimen>
+
+    <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
+    <dimen name="all_apps_label_top_padding">16dp</dimen>
+    <dimen name="all_apps_label_bottom_padding">8dp</dimen>
+    <dimen name="all_apps_label_text_size">14sp</dimen>
+    <dimen name="all_apps_tip_bottom_margin">8dp</dimen>
+    <!-- The size of corner radius of the arrow in the arrow toast. -->
+    <dimen name="arrow_toast_corner_radius">2dp</dimen>
+
 </resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
index c60cf5a..1ddd3f5 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -21,6 +21,8 @@
 
   <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
 
+  <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string>
+
   <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
 </resources>
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
new file mode 100644
index 0000000..c5c4add
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import java.util.function.BiPredicate;
+
+public class LauncherInitListenerEx extends LauncherInitListener {
+
+    public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
+        super(onInitListener);
+    }
+
+    @Override
+    protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+        PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
+        return super.init(launcher, alreadyOnHome);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
new file mode 100644
index 0000000..948f39e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.systemui.shared.system.LauncherEventUtil;
+
+import androidx.core.content.ContextCompat;
+
+/**
+ * All apps tip view aligned just above prediction apps, shown to users that enter all apps for the
+ * first time.
+ */
+public class AllAppsTipView extends AbstractFloatingView {
+
+    private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
+    private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
+    private static final long SHOW_DELAY_MS = 200;
+    private static final long SHOW_DURATION_MS = 300;
+    private static final long HIDE_DURATION_MS = 100;
+
+    private final Launcher mLauncher;
+    private final Handler mHandler = new Handler();
+
+    private AllAppsTipView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    private AllAppsTipView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setOrientation(LinearLayout.VERTICAL);
+
+        mLauncher = Launcher.getLauncher(context);
+
+        init(context);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            close(true);
+        }
+        return false;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (mIsOpen) {
+            if (animate) {
+                animate().alpha(0f)
+                        .withLayer()
+                        .setStartDelay(0)
+                        .setDuration(HIDE_DURATION_MS)
+                        .setInterpolator(Interpolators.ACCEL)
+                        .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
+                        .start();
+            } else {
+                animate().cancel();
+                mLauncher.getDragLayer().removeView(this);
+            }
+            mLauncher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
+            mIsOpen = false;
+        }
+    }
+
+    @Override
+    public void logActionCommand(int command) {
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_ON_BOARD_POPUP) != 0;
+    }
+
+    private void init(Context context) {
+        inflate(context, R.layout.arrow_toast, this);
+
+        TextView textView = findViewById(R.id.text);
+        textView.setText(R.string.all_apps_prediction_tip);
+
+        View dismissButton = findViewById(R.id.dismiss);
+        dismissButton.setOnClickListener(view -> {
+            mLauncher.getUserEventDispatcher().logActionTip(
+                    LauncherEventUtil.DISMISS, ALL_APPS_PREDICTION_TIPS);
+            handleClose(true);
+        });
+
+        View arrowView = findViewById(R.id.arrow);
+        ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
+        ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+                arrowLp.width, arrowLp.height, false));
+        Paint arrowPaint = arrowDrawable.getPaint();
+        TypedValue typedValue = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+        arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
+        // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+        arrowPaint.setPathEffect(new CornerPathEffect(
+                context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
+        arrowView.setBackground(arrowDrawable);
+
+        mIsOpen = true;
+
+        mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+    }
+
+    private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
+        FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
+        if (!floatingHeaderView.hasVisibleContent()
+                || AbstractFloatingView.getOpenView(launcher,
+                TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE) != null
+                || !launcher.isInState(ALL_APPS)
+                || hasSeenAllAppsTip(launcher)
+                || UserManagerCompat.getInstance(launcher).isDemoUser()
+                || ActivityManager.isRunningInTestHarness()) {
+            return false;
+        }
+
+        AllAppsTipView allAppsTipView = new AllAppsTipView(launcher.getAppsView().getContext(),
+            null);
+        launcher.getDragLayer().addView(allAppsTipView);
+
+        DragLayer.LayoutParams params = (DragLayer.LayoutParams) allAppsTipView.getLayoutParams();
+        params.gravity = Gravity.CENTER_HORIZONTAL;
+
+        int top = floatingHeaderView.findFixedRowByType(PredictionRowView.class).getTop();
+        allAppsTipView.setY(top - launcher.getResources().getDimensionPixelSize(
+                R.dimen.all_apps_tip_bottom_margin));
+
+        allAppsTipView.setAlpha(0);
+        allAppsTipView.animate()
+                .alpha(1f)
+                .withLayer()
+                .setStartDelay(SHOW_DELAY_MS)
+                .setDuration(SHOW_DURATION_MS)
+                .setInterpolator(Interpolators.DEACCEL)
+                .start();
+
+        launcher.getUserEventDispatcher().logActionTip(
+                LauncherEventUtil.VISIBLE, ALL_APPS_PREDICTION_TIPS);
+        return true;
+    }
+
+    private static boolean hasSeenAllAppsTip(Launcher launcher) {
+        return launcher.getSharedPrefs().getBoolean(ALL_APPS_TIP_SEEN, false);
+    }
+
+    public static void scheduleShowIfNeeded(Launcher launcher) {
+        if (!hasSeenAllAppsTip(launcher)) {
+            launcher.getStateManager().addStateListener(
+                    new LauncherStateManager.StateListener() {
+                        @Override
+                        public void onStateTransitionStart(LauncherState toState) {
+                        }
+
+                        @Override
+                        public void onStateTransitionComplete(LauncherState finalState) {
+                            if (finalState == ALL_APPS) {
+                                if (showAllAppsTipIfNecessary(launcher)) {
+                                    launcher.getStateManager().removeStateListener(this);
+                                }
+                            }
+                        }
+                    });
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
new file mode 100644
index 0000000..311db21
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -0,0 +1,308 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.FloatingHeaderRow;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.Themes;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+/**
+ * A view which shows a horizontal divider
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class AppsDividerView extends View implements LauncherStateManager.StateListener,
+        FloatingHeaderRow {
+
+    private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
+    private static final int SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT = 20;
+
+    public enum DividerType {
+        NONE,
+        LINE,
+        ALL_APPS_LABEL
+    }
+
+    private final Launcher mLauncher;
+    private final TextPaint mPaint = new TextPaint();
+    private DividerType mDividerType = DividerType.NONE;
+
+    private final @ColorInt int mStrokeColor;
+    private final @ColorInt int mAllAppsLabelTextColor;
+
+    private Layout mAllAppsLabelLayout;
+    private boolean mShowAllAppsLabel;
+
+    private FloatingHeaderView mParent;
+    private boolean mTabsHidden;
+    private FloatingHeaderRow[] mRows = FloatingHeaderRow.NO_ROWS;
+
+    private boolean mIsScrolledOut = false;
+
+    public AppsDividerView(Context context) {
+        this(context, null);
+    }
+
+    public AppsDividerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+
+        boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
+        mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+
+        mStrokeColor = ContextCompat.getColor(context, isMainColorDark
+                ? R.color.all_apps_prediction_row_separator_dark
+                : R.color.all_apps_prediction_row_separator);
+
+        mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
+                ? R.color.all_apps_label_text_dark
+                : R.color.all_apps_label_text);
+    }
+
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+        mParent = parent;
+        mTabsHidden = tabsHidden;
+        mRows = rows;
+        updateDividerType();
+    }
+
+    @Override
+    public int getExpectedHeight() {
+        return getPaddingTop() + getPaddingBottom();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return mDividerType != DividerType.NONE;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return false;
+    }
+
+    private void updateDividerType() {
+        final DividerType dividerType;
+        if (!mTabsHidden) {
+            dividerType = DividerType.NONE;
+        } else {
+            // Check how many sections above me.
+            int sectionCount = 0;
+            for (FloatingHeaderRow row : mRows) {
+                if (row == this) {
+                    break;
+                } else if (row.shouldDraw()) {
+                    sectionCount ++;
+                }
+            }
+
+            if (mShowAllAppsLabel && sectionCount > 0) {
+                dividerType = DividerType.ALL_APPS_LABEL;
+            } else if (sectionCount == 1) {
+                dividerType = DividerType.LINE;
+            } else {
+                dividerType = DividerType.NONE;
+            }
+        }
+
+        if (mDividerType != dividerType) {
+            mDividerType = dividerType;
+            int topPadding;
+            int bottomPadding;
+            switch (dividerType) {
+                case LINE:
+                    topPadding = 0;
+                    bottomPadding = getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
+                    mPaint.setColor(mStrokeColor);
+                    break;
+                case ALL_APPS_LABEL:
+                    topPadding = getAllAppsLabelLayout().getHeight() + getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_label_top_padding);
+                    bottomPadding = getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
+                    mPaint.setColor(mAllAppsLabelTextColor);
+                    break;
+                case NONE:
+                default:
+                    topPadding = bottomPadding = 0;
+                    break;
+            }
+            setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+            updateViewVisibility();
+            invalidate();
+            requestLayout();
+            if (mParent != null) {
+                mParent.onHeightUpdated();
+            }
+        }
+    }
+
+    private void updateViewVisibility() {
+        setVisibility(mDividerType == DividerType.NONE
+                ? GONE
+                : (mIsScrolledOut ? INVISIBLE : VISIBLE));
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mDividerType == DividerType.LINE) {
+            int side = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+            int y = getHeight() - (getPaddingBottom() / 2);
+            int x1 = getPaddingLeft() + side;
+            int x2 = getWidth() - getPaddingRight() - side;
+            canvas.drawLine(x1, y, x2, y, mPaint);
+        } else if (mDividerType == DividerType.ALL_APPS_LABEL) {
+            Layout textLayout = getAllAppsLabelLayout();
+            int x = getWidth() / 2 - textLayout.getWidth() / 2;
+            int y = getHeight() - getPaddingBottom() - textLayout.getHeight();
+            canvas.translate(x, y);
+            textLayout.draw(canvas);
+            canvas.translate(-x, -y);
+        }
+    }
+
+    private Layout getAllAppsLabelLayout() {
+        if (mAllAppsLabelLayout == null) {
+            mPaint.setAntiAlias(true);
+            mPaint.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
+            mPaint.setTextSize(
+                    getResources().getDimensionPixelSize(R.dimen.all_apps_label_text_size));
+
+            CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
+            mAllAppsLabelLayout = StaticLayout.Builder.obtain(
+                    allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
+                    Math.round(mPaint.measureText(allAppsLabelText.toString())))
+                    .setAlignment(Layout.Alignment.ALIGN_CENTER)
+                    .setMaxLines(1)
+                    .setIncludePad(true)
+                    .build();
+        }
+        return mAllAppsLabelLayout;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getPaddingBottom() + getPaddingTop());
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (shouldShowAllAppsLabel()) {
+            mShowAllAppsLabel = true;
+            mLauncher.getStateManager().addStateListener(this);
+            updateDividerType();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getStateManager().removeStateListener(this);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) { }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState == ALL_APPS) {
+            setAllAppsVisitedCount(getAllAppsVisitedCount() + 1);
+        } else {
+            if (mShowAllAppsLabel != shouldShowAllAppsLabel()) {
+                mShowAllAppsLabel = !mShowAllAppsLabel;
+                updateDividerType();
+            }
+
+            if (!mShowAllAppsLabel) {
+                mLauncher.getStateManager().removeStateListener(this);
+            }
+        }
+    }
+
+    private void setAllAppsVisitedCount(int count) {
+        mLauncher.getSharedPrefs().edit().putInt(ALL_APPS_VISITED_COUNT, count).apply();
+    }
+
+    private int getAllAppsVisitedCount() {
+        return mLauncher.getSharedPrefs().getInt(ALL_APPS_VISITED_COUNT, 0);
+    }
+
+    private boolean shouldShowAllAppsLabel() {
+        return getAllAppsVisitedCount() < SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT;
+    }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) {
+        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+                + grid.cellLayoutPaddingLeftRightPx;
+        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+    }
+
+    @Override
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        // Don't use setViewAlpha as we want to control the visibility ourselves.
+        setter.setFloat(this, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+    }
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        setTranslationY(scroll);
+        mIsScrolledOut = isScrolledOut;
+        updateViewVisibility();
+    }
+
+    @Override
+    public Class<AppsDividerView> getTypeClass() {
+        return AppsDividerView.class;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
new file mode 100644
index 0000000..b9f4147
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+
+public class ComponentKeyMapper {
+
+    protected final ComponentKey componentKey;
+    private final Context mContext;
+    private final DynamicItemCache mCache;
+
+    public ComponentKeyMapper(Context context, ComponentKey key, DynamicItemCache cache) {
+        mContext = context;
+        componentKey = key;
+        mCache = cache;
+    }
+
+    public String getPackage() {
+        return componentKey.componentName.getPackageName();
+    }
+
+    public String getComponentClass() {
+        return componentKey.componentName.getClassName();
+    }
+
+    public ComponentKey getComponentKey() {
+        return componentKey;
+    }
+
+    @Override
+    public String toString() {
+        return componentKey.toString();
+    }
+
+    public ItemInfoWithIcon getApp(AllAppsStore store) {
+        AppInfo item = store.getApp(componentKey);
+        if (item != null) {
+            return item;
+        } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
+            return mCache.getInstantApp(componentKey.componentName.getPackageName());
+        } else if (componentKey instanceof ShortcutKey) {
+            return mCache.getShortcutInfo((ShortcutKey) componentKey);
+        }
+        return null;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
new file mode 100644
index 0000000..4ecc39c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.appprediction;
+
+import static android.content.pm.PackageManager.MATCH_INSTANT;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.InstantAppResolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class which loads and caches predicted items like instant apps and shortcuts, before
+ * they can be displayed on the UI
+ */
+public class DynamicItemCache {
+
+    private static final String TAG = "DynamicItemCache";
+    private static final boolean DEBUG = false;
+    private static final String DEFAULT_URL = "default-url";
+
+    private static final int BG_MSG_LOAD_SHORTCUTS = 1;
+    private static final int BG_MSG_LOAD_INSTANT_APPS = 2;
+
+    private static final int UI_MSG_UPDATE_SHORTCUTS = 1;
+    private static final int UI_MSG_UPDATE_INSTANT_APPS = 2;
+
+    private final Context mContext;
+    private final Handler mWorker;
+    private final Handler mUiHandler;
+    private final InstantAppResolver mInstantAppResolver;
+    private final Runnable mOnUpdateCallback;
+
+    private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
+    private final Map<String, InstantAppItemInfo> mInstantApps;
+
+    public DynamicItemCache(Context context, Runnable onUpdateCallback) {
+        mContext = context;
+        mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage);
+        mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
+        mInstantAppResolver = InstantAppResolver.newInstance(context);
+        mOnUpdateCallback = onUpdateCallback;
+
+        mShortcuts = new HashMap<>();
+        mInstantApps = new HashMap<>();
+    }
+
+    public void cacheItems(List<ShortcutKey> shortcutKeys, List<String> pkgNames) {
+        if (!shortcutKeys.isEmpty()) {
+            mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
+            Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
+        }
+        if (!pkgNames.isEmpty()) {
+            mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
+            Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
+        }
+    }
+
+    private boolean handleWorkerMessage(Message msg) {
+        switch (msg.what) {
+            case BG_MSG_LOAD_SHORTCUTS: {
+                List<ShortcutKey> shortcutKeys = msg.obj != null ?
+                        (List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
+                Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
+                for (ShortcutKey shortcutKey : shortcutKeys) {
+                    WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
+                    if (workspaceItemInfo != null) {
+                        shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
+                    }
+                }
+                Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
+                        .sendToTarget();
+                return true;
+            }
+            case BG_MSG_LOAD_INSTANT_APPS: {
+                List<String> pkgNames = msg.obj != null ?
+                        (List<String>) msg.obj : Collections.EMPTY_LIST;
+                List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
+                for (String pkgName : pkgNames) {
+                    InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
+                    if (instantAppItemInfo != null) {
+                        instantAppItemInfos.add(instantAppItemInfo);
+                    }
+                }
+                Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
+                        .sendToTarget();
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean handleUiMessage(Message msg) {
+        switch (msg.what) {
+            case UI_MSG_UPDATE_SHORTCUTS: {
+                mShortcuts.clear();
+                mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
+                mOnUpdateCallback.run();
+                return true;
+            }
+            case UI_MSG_UPDATE_INSTANT_APPS: {
+                List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
+                mInstantApps.clear();
+                for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
+                    mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
+                            instantAppItemInfo);
+                }
+                mOnUpdateCallback.run();
+                if (DEBUG) {
+                    Log.d(TAG, String.format("Cache size: %d, Cache: %s",
+                            mInstantApps.size(), mInstantApps.toString()));
+                }
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @WorkerThread
+    private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
+        DeepShortcutManager mgr = DeepShortcutManager.getInstance(mContext);
+        List<ShortcutInfo> details = mgr.queryForFullDetails(
+                shortcutKey.componentName.getPackageName(),
+                Collections.<String>singletonList(shortcutKey.getId()),
+                shortcutKey.user);
+        if (!details.isEmpty()) {
+            WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
+            try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+                si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
+            } catch (Exception e) {
+                if (DEBUG) {
+                    Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
+                }
+                return null;
+            }
+            return si;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "No shortcut found: " + shortcutKey.toString());
+        }
+        return null;
+    }
+
+    private InstantAppItemInfo loadInstantApp(String pkgName) {
+        PackageManager pm = mContext.getPackageManager();
+
+        try {
+            ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
+            if (!mInstantAppResolver.isInstantApp(ai)) {
+                return null;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+
+        String url = retrieveDefaultUrl(pkgName, pm);
+        if (url == null) {
+            Log.w(TAG, "no default-url available for pkg " + pkgName);
+            return null;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_VIEW)
+                .addCategory(Intent.CATEGORY_BROWSABLE)
+                .setData(Uri.parse(url));
+        InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
+        IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+        iconCache.getTitleAndIcon(info, false);
+        if (info.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, info.user)) {
+            return null;
+        }
+        return info;
+    }
+
+    @Nullable
+    public static String retrieveDefaultUrl(String pkgName, PackageManager pm) {
+        Intent mainIntent = new Intent().setAction(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_LAUNCHER).setPackage(pkgName);
+        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+                mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
+        String url = null;
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            if (resolveInfo.activityInfo.metaData != null
+                    && resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
+                url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
+            }
+        }
+        return url;
+    }
+
+    @UiThread
+    public InstantAppItemInfo getInstantApp(String pkgName) {
+        return mInstantApps.get(pkgName);
+    }
+
+    @MainThread
+    public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
+        return mShortcuts.get(key);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
new file mode 100644
index 0000000..6e5f461
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.content.ComponentName;
+import android.content.Intent;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.WorkspaceItemInfo;
+
+public class InstantAppItemInfo extends AppInfo {
+
+    public InstantAppItemInfo(Intent intent, String packageName) {
+        this.intent = intent;
+        this.componentName = new ComponentName(packageName, COMPONENT_CLASS_MARKER);
+    }
+
+    @Override
+    public ComponentName getTargetComponent() {
+        return componentName;
+    }
+
+    @Override
+    public WorkspaceItemInfo makeWorkspaceItem() {
+        WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem();
+        workspaceItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON
+                | WorkspaceItemInfo.FLAG_RESTORE_STARTED
+                | WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+        workspaceItemInfo.intent.setPackage(componentName.getPackageName());
+        return workspaceItemInfo;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
new file mode 100644
index 0000000..bd78573
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -0,0 +1,214 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.appprediction;
+
+import static com.android.launcher3.appprediction.PredictionUiStateManager.KEY_APP_SUGGESTION;
+
+import android.annotation.TargetApi;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.util.UiThreadHelper;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class PredictionAppTracker extends AppLaunchTracker
+        implements OnSharedPreferenceChangeListener {
+
+    private static final String TAG = "PredictionAppTracker";
+    private static final boolean DBG = false;
+
+    private static final int MSG_INIT = 0;
+    private static final int MSG_DESTROY = 1;
+    private static final int MSG_LAUNCH = 2;
+    private static final int MSG_PREDICT = 3;
+
+    private final Context mContext;
+    private final Handler mMessageHandler;
+
+    private boolean mEnabled;
+
+    // Accessed only on worker thread
+    private AppPredictor mHomeAppPredictor;
+    private AppPredictor mRecentsOverviewPredictor;
+
+    public PredictionAppTracker(Context context) {
+        mContext = context;
+        mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);
+
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
+        prefs.registerOnSharedPreferenceChangeListener(this);
+        InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
+    }
+
+    @UiThread
+    private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+        // Reinitialize everything
+        setEnabled(mEnabled);
+    }
+
+    @Override
+    @UiThread
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        if (KEY_APP_SUGGESTION.equals(key)) {
+            setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
+        }
+    }
+
+    @WorkerThread
+    private void destroy() {
+        if (mHomeAppPredictor != null) {
+            mHomeAppPredictor.destroy();
+            mHomeAppPredictor = null;
+        }
+        if (mRecentsOverviewPredictor != null) {
+            mRecentsOverviewPredictor.destroy();
+            mRecentsOverviewPredictor = null;
+        }
+    }
+
+    @WorkerThread
+    private AppPredictor createPredictor(Client client, int count) {
+        AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
+
+        AppPredictor predictor = apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(mContext)
+                        .setUiSurface(client.id)
+                        .setPredictedTargetCount(count)
+                        .build());
+        predictor.registerPredictionUpdates(mContext.getMainExecutor(),
+                PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client));
+        predictor.requestPredictionUpdate();
+        return predictor;
+    }
+
+    @WorkerThread
+    private boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_INIT: {
+                // Destroy any existing clients
+                destroy();
+
+                // Initialize the clients
+                int count = InvariantDeviceProfile.INSTANCE.get(mContext).numColumns;
+                mHomeAppPredictor = createPredictor(Client.HOME, count);
+                mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
+                return true;
+            }
+            case MSG_DESTROY: {
+                destroy();
+                return true;
+            }
+            case MSG_LAUNCH: {
+                if (mEnabled && mHomeAppPredictor != null) {
+                    mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
+                }
+                return true;
+            }
+            case MSG_PREDICT: {
+                if (mEnabled && mHomeAppPredictor != null) {
+                    String client = (String) msg.obj;
+                    if (Client.HOME.id.equals(client)) {
+                        mHomeAppPredictor.requestPredictionUpdate();
+                    } else {
+                        mRecentsOverviewPredictor.requestPredictionUpdate();
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    @UiThread
+    public void onReturnedToHome() {
+        String client = Client.HOME.id;
+        mMessageHandler.removeMessages(MSG_PREDICT, client);
+        Message.obtain(mMessageHandler, MSG_PREDICT, client).sendToTarget();
+        if (DBG) {
+            Log.d(TAG, String.format("Sent immediate message to update %s", client));
+        }
+    }
+
+    @UiThread
+    public void setEnabled(boolean isEnabled) {
+        mEnabled = isEnabled;
+        if (isEnabled) {
+            mMessageHandler.removeMessages(MSG_DESTROY);
+            mMessageHandler.sendEmptyMessage(MSG_INIT);
+        } else {
+            mMessageHandler.removeMessages(MSG_INIT);
+            mMessageHandler.sendEmptyMessage(MSG_DESTROY);
+        }
+    }
+
+    @Override
+    @UiThread
+    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
+            String container) {
+        // TODO: Use the full shortcut info
+        AppTarget target = new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutId))
+                .setTarget(packageName, user)
+                .setClassName(shortcutId)
+                .build();
+        sendLaunch(target, container);
+    }
+
+    @Override
+    @UiThread
+    public void onStartApp(ComponentName cn, UserHandle user, String container) {
+        if (cn != null) {
+            AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn))
+                    .setTarget(cn.getPackageName(), user)
+                    .setClassName(cn.getClassName())
+                    .build();
+            sendLaunch(target, container);
+        }
+    }
+
+    @UiThread
+    private void sendLaunch(AppTarget target, String container) {
+        AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH)
+                .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
+                .build();
+        Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
new file mode 100644
index 0000000..55f4c98
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -0,0 +1,411 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.IntProperty;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.FloatingHeaderRow;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.keyboard.FocusIndicatorHelper;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.AnimatedFloat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+@TargetApi(Build.VERSION_CODES.P)
+public class PredictionRowView extends LinearLayout implements
+        LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
+
+    private static final String TAG = "PredictionRowView";
+
+    private static final IntProperty<PredictionRowView> TEXT_ALPHA =
+            new IntProperty<PredictionRowView>("textAlpha") {
+                @Override
+                public void setValue(PredictionRowView view, int alpha) {
+                    view.setTextAlpha(alpha);
+                }
+
+                @Override
+                public Integer get(PredictionRowView view) {
+                    return view.mIconCurrentTextAlpha;
+                }
+            };
+
+    private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
+            (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
+
+    private static final OnClickListener PREDICTION_CLICK_LISTENER =
+            ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
+
+    private final Launcher mLauncher;
+    private final PredictionUiStateManager mPredictionUiStateManager;
+    private final int mNumPredictedAppsPerRow;
+
+    // The set of predicted app component names
+    private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>();
+    // The set of predicted apps resolved from the component names and the current set of apps
+    private final ArrayList<ItemInfoWithIcon> mPredictedApps = new ArrayList<>();
+    // Helper to drawing the focus indicator.
+    private final FocusIndicatorHelper mFocusHelper;
+
+    private final int mIconTextColor;
+    private final int mIconFullTextAlpha;
+    private int mIconCurrentTextAlpha;
+
+    private FloatingHeaderView mParent;
+    private boolean mScrolledOut;
+
+    private float mScrollTranslation = 0;
+    private final AnimatedFloat mContentAlphaFactor =
+            new AnimatedFloat(this::updateTranslationAndAlpha);
+    private final AnimatedFloat mOverviewScrollFactor =
+            new AnimatedFloat(this::updateTranslationAndAlpha);
+
+    private View mLoadingProgress;
+
+    private boolean mPredictionsEnabled = false;
+
+    public PredictionRowView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        setOrientation(LinearLayout.HORIZONTAL);
+
+        mFocusHelper = new SimpleFocusIndicatorHelper(this);
+
+        mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numColumns;
+        mLauncher = Launcher.getLauncher(context);
+        mLauncher.addOnDeviceProfileChangeListener(this);
+
+        mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(context);
+
+        mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
+        mIconFullTextAlpha = Color.alpha(mIconTextColor);
+        mIconCurrentTextAlpha = mIconFullTextAlpha;
+
+        updateVisibility();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        mPredictionUiStateManager.setTargetAppsView(mLauncher.getAppsView());
+        getAppsStore().registerIconContainer(this);
+        AllAppsTipView.scheduleShowIfNeeded(mLauncher);
+    }
+
+    private AllAppsStore getAppsStore() {
+        return mLauncher.getAppsView().getAppsStore();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        mPredictionUiStateManager.setTargetAppsView(null);
+        getAppsStore().unregisterIconContainer(this);
+    }
+
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+        mParent = parent;
+        setPredictionsEnabled(mPredictionUiStateManager.arePredictionsEnabled());
+    }
+
+    private void setPredictionsEnabled(boolean predictionsEnabled) {
+        if (predictionsEnabled != mPredictionsEnabled) {
+            mPredictionsEnabled = predictionsEnabled;
+            updateVisibility();
+        }
+    }
+
+    private void updateVisibility() {
+        setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
+                MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mFocusHelper.draw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    public int getExpectedHeight() {
+        return getVisibility() == GONE ? 0 :
+                Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
+                + getPaddingTop() + getPaddingBottom();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return getVisibility() != GONE;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return mPredictionUiStateManager.arePredictionsEnabled();
+    }
+
+    /**
+     * Returns the predicted apps.
+     */
+    public List<ItemInfoWithIcon> getPredictedApps() {
+        return mPredictedApps;
+    }
+
+    /**
+     * Sets the current set of predicted apps.
+     *
+     * This can be called before we get the full set of applications, we should merge the results
+     * only in onPredictionsUpdated() which is idempotent.
+     *
+     * If the number of predicted apps is the same as the previous list of predicted apps,
+     * we can optimize by swapping them in place.
+     */
+    public void setPredictedApps(boolean predictionsEnabled, List<ComponentKeyMapper> apps) {
+        setPredictionsEnabled(predictionsEnabled);
+        mPredictedAppComponents.clear();
+        mPredictedAppComponents.addAll(apps);
+
+        mPredictedApps.clear();
+        mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
+        applyPredictionApps();
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        removeAllViews();
+        applyPredictionApps();
+    }
+
+    private void applyPredictionApps() {
+        if (mLoadingProgress != null) {
+            removeView(mLoadingProgress);
+        }
+        if (!mPredictionsEnabled) {
+            mParent.onHeightUpdated();
+            return;
+        }
+
+        if (getChildCount() != mNumPredictedAppsPerRow) {
+            while (getChildCount() > mNumPredictedAppsPerRow) {
+                removeViewAt(0);
+            }
+            while (getChildCount() < mNumPredictedAppsPerRow) {
+                BubbleTextView icon = (BubbleTextView) mLauncher.getLayoutInflater().inflate(
+                        R.layout.all_apps_icon, this, false);
+                icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
+                icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
+                icon.setLongPressTimeoutFactor(1f);
+                icon.setOnFocusChangeListener(mFocusHelper);
+
+                LayoutParams lp = (LayoutParams) icon.getLayoutParams();
+                // Ensure the all apps icon height matches the workspace icons in portrait mode.
+                lp.height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+                lp.width = 0;
+                lp.weight = 1;
+                addView(icon);
+            }
+        }
+
+        int predictionCount = mPredictedApps.size();
+        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
+
+        for (int i = 0; i < getChildCount(); i++) {
+            BubbleTextView icon = (BubbleTextView) getChildAt(i);
+            icon.reset();
+            if (predictionCount > i) {
+                icon.setVisibility(View.VISIBLE);
+                if (mPredictedApps.get(i) instanceof AppInfo) {
+                    icon.applyFromApplicationInfo((AppInfo) mPredictedApps.get(i));
+                } else if (mPredictedApps.get(i) instanceof WorkspaceItemInfo) {
+                    icon.applyFromWorkspaceItem((WorkspaceItemInfo) mPredictedApps.get(i));
+                }
+                icon.setTextColor(iconColor);
+            } else {
+                icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
+            }
+        }
+
+        if (predictionCount == 0) {
+            if (mLoadingProgress == null) {
+                mLoadingProgress = LayoutInflater.from(getContext())
+                        .inflate(R.layout.prediction_load_progress, this, false);
+            }
+            addView(mLoadingProgress);
+        } else {
+            mLoadingProgress = null;
+        }
+
+        mParent.onHeightUpdated();
+    }
+
+    private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
+        if (getAppsStore().getApps().isEmpty()) {
+            // Apps have not been bound yet.
+            return Collections.emptyList();
+        }
+
+        List<ItemInfoWithIcon> predictedApps = new ArrayList<>();
+        for (ComponentKeyMapper mapper : components) {
+            ItemInfoWithIcon info = mapper.getApp(getAppsStore());
+            if (info != null) {
+                predictedApps.add(info);
+            } else {
+                if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                    Log.e(TAG, "Predicted app not found: " + mapper);
+                }
+            }
+            // Stop at the number of predicted apps
+            if (predictedApps.size() == mNumPredictedAppsPerRow) {
+                break;
+            }
+        }
+        return predictedApps;
+    }
+
+    @Override
+    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
+        for (int i = 0; i < mPredictedApps.size(); i++) {
+            ItemInfoWithIcon appInfo = mPredictedApps.get(i);
+            if (appInfo == info) {
+                targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
+                target.predictedRank = i;
+                break;
+            }
+        }
+    }
+
+    public void setTextAlpha(int alpha) {
+        mIconCurrentTextAlpha = alpha;
+        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
+
+        if (mLoadingProgress == null) {
+            for (int i = 0; i < getChildCount(); i++) {
+                ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
+            }
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        mScrolledOut = isScrolledOut;
+        updateTranslationAndAlpha();
+        if (!isScrolledOut) {
+            mScrollTranslation = scroll;
+            updateTranslationAndAlpha();
+        }
+    }
+
+    private void updateTranslationAndAlpha() {
+        if (mPredictionsEnabled) {
+            setTranslationY((1 - mOverviewScrollFactor.value) * mScrollTranslation);
+
+            float factor = ALPHA_FACTOR_INTERPOLATOR.getInterpolation(mOverviewScrollFactor.value);
+            float endAlpha = factor + (1 - factor) * (mScrolledOut ? 0 : 1);
+            setAlpha(mContentAlphaFactor.value * endAlpha);
+            AlphaUpdateListener.updateVisibility(this);
+        }
+    }
+
+    @Override
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        boolean isDrawn = getAlpha() > 0;
+        int textAlpha = hasHeaderExtra
+                ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility
+                : mIconCurrentTextAlpha; // Leave as before
+        if (!isDrawn) {
+            // If the header is not drawn, no need to animate the text alpha
+            setTextAlpha(textAlpha);
+        } else {
+            setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator);
+        }
+
+        setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
+                (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR);
+        setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
+                fadeInterpolator);
+    }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) {
+        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+                + grid.cellLayoutPaddingLeftRightPx;
+        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+    }
+
+    @Override
+    public Class<PredictionRowView> getTypeClass() {
+        return PredictionRowView.class;
+    }
+}
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
new file mode 100644
index 0000000..54fd845
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -0,0 +1,330 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.appprediction;
+
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Handler responsible to updating the UI due to predicted apps changes. Operations:
+ * 1) Pushes the predicted apps to all-apps. If all-apps is visible, waits until it becomes
+ * invisible again before applying the changes. This ensures that the UI does not change abruptly
+ * in front of the user, even if an app launched and user pressed back button to return to the
+ * all-apps UI again.
+ * 2) Prefetch high-res icons for predicted apps. This ensures that we have the icons in memory
+ * even if all-apps is not opened as they are shown in search UI as well
+ * 3) Load instant app if it is not already in memory. As predictions are persisted on disk,
+ * instant app will not be in memory when launcher starts.
+ * 4) Maintains the current active client id (for the predictions) and all updates are performed on
+ * that client id.
+ */
+public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
+        OnSharedPreferenceChangeListener, OnIDPChangeListener, OnUpdateListener {
+
+    public static final String KEY_APP_SUGGESTION = "pref_show_predictions";
+
+    // TODO (b/129421797): Update the client constants
+    public enum Client {
+        HOME("GEL"),
+        OVERVIEW("OVERVIEW_GEL");
+
+        public final String id;
+
+        Client(String id) {
+            this.id = id;
+        }
+    }
+
+    public static final MainThreadInitializedObject<PredictionUiStateManager> INSTANCE =
+            new MainThreadInitializedObject<>(PredictionUiStateManager::new);
+
+    private final Context mContext;
+    private final SharedPreferences mMainPrefs;
+
+    private final DynamicItemCache mDynamicItemCache;
+    private final List[] mPredictionServicePredictions;
+
+    private int mMaxIconsPerRow;
+    private Client mActiveClient;
+
+    private AllAppsContainerView mAppsView;
+
+    private PredictionState mPendingState;
+    private PredictionState mCurrentState;
+
+    private PredictionUiStateManager(Context context) {
+        mContext = context;
+        mMainPrefs = Utilities.getPrefs(context);
+
+        mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
+
+        mActiveClient = Client.HOME;
+
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+        mMaxIconsPerRow = idp.numColumns;
+
+        idp.addOnChangeListener(this);
+        mPredictionServicePredictions = new List[Client.values().length];
+        for (int i = 0; i < mPredictionServicePredictions.length; i++) {
+            mPredictionServicePredictions[i] = Collections.emptyList();
+        }
+        // Listens for enable/disable signal, and predictions if using AiAi is disabled.
+        mMainPrefs.registerOnSharedPreferenceChangeListener(this);
+        // Call this last
+        mCurrentState = parseLastState();
+    }
+
+    @Override
+    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+        mMaxIconsPerRow = profile.numColumns;
+    }
+
+    public Client getClient() {
+        return mActiveClient;
+    }
+
+    public void switchClient(Client client) {
+        if (client == mActiveClient) {
+            return;
+        }
+        mActiveClient = client;
+        dispatchOnChange(true);
+    }
+
+    public void setTargetAppsView(AllAppsContainerView appsView) {
+        if (mAppsView != null) {
+            mAppsView.getAppsStore().removeUpdateListener(this);
+        }
+        mAppsView = appsView;
+        if (mAppsView != null) {
+            mAppsView.getAppsStore().addUpdateListener(this);
+        }
+        if (mPendingState != null) {
+            applyState(mPendingState);
+            mPendingState = null;
+        } else {
+            applyState(mCurrentState);
+        }
+        updateDependencies(mCurrentState);
+    }
+
+    @Override
+    public void reapplyItemInfo(ItemInfoWithIcon info) { }
+
+    @Override
+    public void onGlobalLayout() {
+        if (mAppsView == null) {
+            return;
+        }
+        if (mPendingState != null && canApplyPredictions(mPendingState)) {
+            applyState(mPendingState);
+            mPendingState = null;
+        }
+        if (mPendingState == null) {
+            mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        }
+    }
+
+    private void scheduleApplyPredictedApps(PredictionState state) {
+        boolean registerListener = mPendingState == null;
+        mPendingState = state;
+        if (registerListener) {
+            // OnGlobalLayoutListener is called whenever a view in the view tree changes
+            // visibility. Add a listener and wait until appsView is invisible again.
+            mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+        }
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        if (KEY_APP_SUGGESTION.equals(key)) {
+            dispatchOnChange(true);
+        }
+    }
+
+    private void applyState(PredictionState state) {
+        boolean wasEnabled = mCurrentState.isEnabled;
+        mCurrentState = state;
+        if (mAppsView != null) {
+            mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
+                    .setPredictedApps(mCurrentState.isEnabled, mCurrentState.apps);
+
+            if (wasEnabled != mCurrentState.isEnabled) {
+                // Reapply state as the State UI might have changed.
+                Launcher.getLauncher(mAppsView.getContext()).getStateManager().reapplyState(true);
+            }
+        }
+    }
+
+    public AppPredictor.Callback appPredictorCallback(Client client) {
+        return targets -> {
+            mPredictionServicePredictions[client.ordinal()] = targets;
+            dispatchOnChange(true);
+        };
+    }
+
+    private void dispatchOnChange(boolean changed) {
+        PredictionState newState = changed ? parseLastState() :
+                (mPendingState == null ? mCurrentState : mPendingState);
+        if (changed && mAppsView != null && !canApplyPredictions(newState)) {
+            scheduleApplyPredictedApps(newState);
+        } else {
+            applyState(newState);
+        }
+    }
+
+    private PredictionState parseLastState() {
+        PredictionState state = new PredictionState();
+        state.isEnabled = mMainPrefs.getBoolean(KEY_APP_SUGGESTION, true);
+        if (!state.isEnabled) {
+            state.apps = Collections.EMPTY_LIST;
+            return state;
+        }
+
+        state.apps = new ArrayList<>();
+
+        List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
+        if (!appTargets.isEmpty()) {
+            for (AppTarget appTarget : appTargets) {
+                ComponentKey key;
+                if (appTarget.getShortcutInfo() != null) {
+                    key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
+                } else {
+                    key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
+                            appTarget.getClassName()), appTarget.getUser());
+                }
+                state.apps.add(new ComponentKeyMapper(mContext, key, mDynamicItemCache));
+            }
+        }
+        updateDependencies(state);
+        return state;
+    }
+
+    private void updateDependencies(PredictionState state) {
+        if (!state.isEnabled || mAppsView == null) {
+            return;
+        }
+
+        IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+        List<String> instantAppsToLoad = new ArrayList<>();
+        List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
+        int total = state.apps.size();
+        for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) {
+            ComponentKeyMapper mapper = state.apps.get(i);
+            // Update instant apps
+            if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
+                instantAppsToLoad.add(mapper.getPackage());
+                count++;
+            } else if (mapper.getComponentKey() instanceof ShortcutKey) {
+                shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
+                count++;
+            } else {
+                // Reload high res icon
+                AppInfo info = (AppInfo) mapper.getApp(mAppsView.getAppsStore());
+                if (info != null) {
+                    if (info.usingLowResIcon()) {
+                        // TODO: Update icon cache to support null callbacks.
+                        iconCache.updateIconInBackground(this, info);
+                    }
+                    count++;
+                }
+            }
+        }
+        mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad);
+    }
+
+    @Override
+    public void onAppsUpdated() {
+        dispatchOnChange(false);
+    }
+
+    public boolean arePredictionsEnabled() {
+        return mCurrentState.isEnabled;
+    }
+
+    private boolean canApplyPredictions(PredictionState newState) {
+        if (mAppsView == null) {
+            // If there is no apps view, no need to schedule.
+            return true;
+        }
+        Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
+        PredictionRowView predictionRow = mAppsView.getFloatingHeaderView().
+                findFixedRowByType(PredictionRowView.class);
+        if (!predictionRow.isShown() || predictionRow.getAlpha() == 0 ||
+                launcher.isForceInvisible()) {
+            return true;
+        }
+
+        if (mCurrentState.isEnabled != newState.isEnabled
+                || mCurrentState.apps.isEmpty() != newState.apps.isEmpty()) {
+            // If the visibility of the prediction row is changing, apply immediately.
+            return true;
+        }
+
+        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+            // If we are here & mAppsView.isShown() = true, we are probably in all-apps or mid way
+            return false;
+        }
+        if (!launcher.isInState(OVERVIEW) && !launcher.isInState(BACKGROUND_APP)) {
+            // Just a fallback as we dont need to apply instantly, if we are not in the swipe-up UI
+            return false;
+        }
+
+        // Instead of checking against 1, we should check against (1 + delta), where delta accounts
+        // for the nav-bar height (as app icon can still be visible under the nav-bar). Checking
+        // against 1, keeps the logic simple :)
+        return launcher.getAllAppsController().getProgress() > 1;
+    }
+
+    public PredictionState getCurrentState() {
+        return mCurrentState;
+    }
+
+    public static class PredictionState {
+
+        public boolean isEnabled;
+        public List<ComponentKeyMapper> apps;
+    }
+}
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 0d5574f..11a1885 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
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
@@ -22,15 +24,17 @@
 import android.os.Build;
 import android.util.FloatProperty;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 
-import androidx.annotation.NonNull;
-
 /**
  * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
  * the basic view properties, this class also manages changes in the task visuals.
@@ -49,10 +53,8 @@
         if (state.overviewUi) {
             mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
-            mRecentsView.setHintVisibility(1);
-        } else {
-            mRecentsView.setHintVisibility(0);
         }
+        setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher));
     }
 
     @Override
@@ -62,7 +64,6 @@
 
         if (!toState.overviewUi) {
             builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
-            mRecentsView.setHintVisibility(0);
         }
 
         if (toState.overviewUi) {
@@ -74,8 +75,15 @@
             updateAnim.setDuration(config.duration);
             builder.play(updateAnim);
             mRecentsView.updateEmptyMessage();
-            builder.addOnFinishRunnable(() -> mRecentsView.setHintVisibility(1));
         }
+
+        setAlphas(config.getPropertySetter(builder), toState.getVisibleElements(mLauncher));
+    }
+
+    private void setAlphas(PropertySetter propertySetter, int visibleElements) {
+        boolean hasClearAllButton = (visibleElements & RECENTS_CLEAR_ALL_BUTTON) != 0;
+        propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
+                hasClearAllButton ? 1f : 0f, LINEAR);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 462e630..140e45c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -18,11 +18,10 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 
 import android.os.RemoteException;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -75,4 +74,8 @@
         return new ScaleAndTranslation(scale, 0f, 0f);
     }
 
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return super.getVisibleElements(launcher) & ~RECENTS_CLEAR_ALL_BUTTON;
+    }
 }
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 94c1545..cda03dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,8 +17,6 @@
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.LoggerUtils.getTargetStr;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
@@ -27,7 +25,6 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
@@ -74,6 +71,15 @@
     }
 
     @Override
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
+        if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
+            // If the hotseat icons are visible in overview, keep them in their normal position.
+            return super.getWorkspaceScaleAndTranslation(launcher);
+        }
+        return getWorkspaceScaleAndTranslation(launcher);
+    }
+
+    @Override
     public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
         return new ScaleAndTranslation(1f, 0f, 0f);
     }
@@ -110,9 +116,9 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return VERTICAL_SWIPE_INDICATOR;
+            return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
         } else {
-            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR |
+            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
                     (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
                             ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 6dd5e21..6358109 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -28,6 +28,7 @@
 import android.animation.AnimatorSet;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
@@ -44,12 +45,14 @@
     private static final long PEEK_ANIM_DURATION = 100;
 
     private final MotionPauseDetector mMotionPauseDetector;
+    private final float mMotionPauseMinDisplacement;
 
     private AnimatorSet mPeekAnim;
 
     public FlingAndHoldTouchController(Launcher l) {
         super(l, false /* allowDragToOverview */);
         mMotionPauseDetector = new MotionPauseDetector(l);
+        mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
     }
 
     @Override
@@ -98,7 +101,8 @@
 
     @Override
     public boolean onDrag(float displacement, MotionEvent event) {
-        mMotionPauseDetector.addPosition(displacement, 0, event.getEventTime());
+        mMotionPauseDetector.setDisallowPause(-displacement < mMotionPauseMinDisplacement);
+        mMotionPauseDetector.addPosition(displacement, event.getEventTime());
         return super.onDrag(displacement, event);
     }
 
@@ -111,6 +115,7 @@
 
             AnimatorSetBuilder builder = new AnimatorSetBuilder();
             builder.setInterpolator(AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+            builder.setInterpolator(AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE, OVERSHOOT_1_2);
             AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
                     NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
             overviewAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 21ddfc0..a6c4445 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -17,8 +17,6 @@
 
 import static android.view.View.TRANSLATION_X;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -44,9 +42,9 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Command;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.views.RecentsView;
@@ -212,11 +210,13 @@
     private void onSwipeInteractionCompleted(LauncherState targetState) {
         clearState();
         mLauncher.getStateManager().goToState(targetState, false /* animated */);
+        AccessibilityManagerCompat.sendStateEventToTest(mLauncher, targetState.ordinal);
     }
 
     private void logStateChange(int startContainerType, int logAction) {
         mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
                 LauncherLogProto.Action.Direction.UP,
+                mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
                 LauncherLogProto.ContainerType.NAVBAR,
                 startContainerType,
                 mEndState.containerType,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 5337c39..20a2487 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -75,7 +75,8 @@
      * @return the animation
      */
     PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
-        TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+        mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
+        TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage());
         if (taskView == null) {
             throw new IllegalStateException("There is no task view to animate to.");
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index ad916be..a76ecd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -21,10 +21,11 @@
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE_NOOP;
+
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPLEFT;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPRIGHT;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE_NOOP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
 
 import android.animation.ValueAnimator;
@@ -35,12 +36,15 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.quickstep.util.MotionPauseDetector;
 import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.launcher3.R;
+import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.NavigationBarCompat;
 
 /**
@@ -76,27 +80,32 @@
     private float mLastProgress;
     private int mState;
     private int mDirection;
+    private ActivityControlHelper mActivityControlHelper;
 
     private final float mDistThreshold;
     private final long mTimeThreshold;
     private final int mAngleThreshold;
     private final float mSlop;
-    private final MotionPauseDetector mMotionPauseDetector;
     private final ISystemUiProxy mSysUiProxy;
     private final InputConsumer mConsumerDelegate;
     private final Context mContext;
 
+    private final InputMonitorCompat mInputMonitorCompat;
+
+
     public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
-            InputConsumer delegate) {
+            InputConsumer delegate, InputMonitorCompat inputMonitorCompat,
+            ActivityControlHelper activityControlHelper) {
         final Resources res = context.getResources();
         mContext = context;
         mSysUiProxy = systemUiProxy;
         mConsumerDelegate = delegate;
-        mMotionPauseDetector = new MotionPauseDetector(context);
         mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
         mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
         mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
-        mSlop = NavigationBarCompat.getQuickScrubTouchSlopPx();
+        mSlop = NavigationBarCompat.getQuickStepDragSlopPx();
+        mInputMonitorCompat = inputMonitorCompat;
+        mActivityControlHelper = activityControlHelper;
         mState = STATE_INACTIVE;
     }
 
@@ -106,8 +115,18 @@
     }
 
     @Override
-    public boolean isActive() {
-        return mState != STATE_INACTIVE;
+    public boolean useSharedSwipeState() {
+        if (mConsumerDelegate != null) {
+            return mConsumerDelegate.useSharedSwipeState();
+        }
+        return false;
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        if (mConsumerDelegate != null) {
+            mConsumerDelegate.onConsumerAboutToBeSwitched();
+        }
     }
 
     @Override
@@ -120,14 +139,6 @@
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
                 mTimeFraction = 0;
-
-                // Detect when the gesture decelerates to start the assistant
-                mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
-                    if (isPaused && mState == STATE_ASSISTANT_ACTIVE) {
-                        mTimeFraction = 1;
-                        updateAssistantProgress();
-                    }
-                });
                 break;
             }
             case ACTION_POINTER_UP: {
@@ -156,6 +167,10 @@
                 if (!mPassedSlop) {
                     // Normal gesture, ensure we pass the slop before we start tracking the gesture
                     if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {
+
+                        // Cancel touches to other windows (intercept)
+                        mInputMonitorCompat.pilferPointers();
+
                         mPassedSlop = true;
                         mStartDragPos.set(mLastPos.x, mLastPos.y);
                         mDragTime = SystemClock.uptimeMillis();
@@ -165,7 +180,8 @@
                                 Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x));
                         mDirection = angle > 90 ? UPLEFT : UPRIGHT;
                         angle = angle > 90 ? 180 - angle : angle;
-                        if (angle > mAngleThreshold) {
+
+                        if (angle > mAngleThreshold && angle < 90) {
                             mState = STATE_ASSISTANT_ACTIVE;
 
                             if (mConsumerDelegate != null) {
@@ -183,7 +199,6 @@
                     // Movement
                     mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
                             mLastPos.y - mStartDragPos.y);
-                    mMotionPauseDetector.addPosition(mDistance, 0, ev.getEventTime());
                     if (mDistance >= 0) {
                         final long diff = SystemClock.uptimeMillis() - mDragTime;
                         mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
@@ -212,7 +227,8 @@
                     animator.setInterpolator(Interpolators.DEACCEL_2);
                     animator.start();
                 }
-                mMotionPauseDetector.clear();
+                mPassedSlop = false;
+                mState = STATE_INACTIVE;
                 break;
         }
 
@@ -232,6 +248,14 @@
                             SWIPE, mDirection, NAVBAR);
                     Bundle args = new Bundle();
                     args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+
+                    BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity();
+                    if (launcherActivity != null) {
+                        launcherActivity.getRootView().
+                                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+                    }
+
                     mSysUiProxy.startAssistant(args);
                     mLaunchedAssistant = true;
                 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
index d919a3e..7fd09f7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
@@ -17,7 +17,9 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.PointF;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 /**
  * A dummy input consumer used when the device is still locked, e.g. from secure camera.
@@ -25,9 +27,13 @@
 public class DeviceLockedInputConsumer implements InputConsumer {
 
     private final Context mContext;
+    private final float mTouchSlopSquared;
+    private final PointF mTouchDown = new PointF();
 
     public DeviceLockedInputConsumer(Context context) {
         mContext = context;
+        float touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mTouchSlopSquared = touchSlop * touchSlop;
     }
 
     @Override
@@ -37,11 +43,19 @@
 
     @Override
     public void onMotionEvent(MotionEvent ev) {
-        // For now, just start the home intent so user is prompted to unlock the device.
+        float x = ev.getX();
+        float y = ev.getY();
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mContext.startActivity(new Intent(Intent.ACTION_MAIN)
-                    .addCategory(Intent.CATEGORY_HOME)
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+            mTouchDown.set(x, y);
+        } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
+            float xSquared = (x - mTouchDown.x) * (x - mTouchDown.x);
+            float ySquared = (y - mTouchDown.y) * (y - mTouchDown.y);
+            if (xSquared + ySquared > mTouchSlopSquared) {
+                // For now, just start the home intent so user is prompted to unlock the device.
+                mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Intent.CATEGORY_HOME)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+            }
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index 21e98f2..f12efc8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -26,11 +26,13 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.RecentsView;
@@ -39,9 +41,6 @@
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 /**
  * {@link ActivityControlHelper} for recents when the default launcher is different than the
  * currently running one and apps should interact with the {@link RecentsActivity} as opposed
@@ -72,7 +71,9 @@
 
     @Override
     public void onSwipeUpComplete(RecentsActivity activity) {
-        // TODO:
+        RecentsView recentsView = activity.getOverviewPanel();
+        recentsView.getClearAllButton().setVisibilityAlpha(1);
+        recentsView.setDisallowScrollToClearAll(false);
     }
 
     @Override
@@ -121,6 +122,8 @@
 
         RecentsView rv = activity.getOverviewPanel();
         rv.setContentAlpha(0);
+        rv.getClearAllButton().setVisibilityAlpha(0);
+        rv.setDisallowScrollToClearAll(true);
 
         return new AnimationFactory() {
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index ad9fe78..e3f9e02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -34,7 +34,7 @@
 
     int getType();
 
-    default boolean isActive() {
+    default boolean useSharedSwipeState() {
         return false;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 621d1b0..35783b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -38,10 +38,14 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherInitListenerEx;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -59,10 +63,6 @@
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
 /**
  * {@link ActivityControlHelper} for the in-launcher recents.
  */
@@ -163,21 +163,16 @@
         }
         activity.getStateManager().setRestState(resetState);
 
-        final LauncherState fromState;
-        if (!activityVisible) {
-            // Since the launcher is not visible, we can safely reset the scroll position.
-            // This ensures then the next swipe up to all-apps starts from scroll 0.
-            activity.getAppsView().reset(false /* animate */);
-            fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
-            activity.getStateManager().goToState(fromState, false);
+        final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
+        activity.getStateManager().goToState(fromState, false);
+        // Since all apps is not visible, we can safely reset the scroll position.
+        // This ensures then the next swipe up to all-apps starts from scroll 0.
+        activity.getAppsView().reset(false /* animate */);
 
-            // Optimization, hide the all apps view to prevent layout while initializing
-            activity.getAppsView().getContentView().setVisibility(View.GONE);
+        // Optimization, hide the all apps view to prevent layout while initializing
+        activity.getAppsView().getContentView().setVisibility(View.GONE);
 
-            AccessibilityManagerCompat.sendStateEventToTest(activity, fromState.ordinal);
-        } else {
-            fromState = startState;
-        }
+        AccessibilityManagerCompat.sendStateEventToTest(activity, fromState.ordinal);
 
         return new AnimationFactory() {
             private Animator mShelfAnim;
@@ -185,8 +180,7 @@
 
             @Override
             public void createActivityController(long transitionLength) {
-                createActivityControllerInternal(activity, activityVisible, fromState,
-                        transitionLength, callback);
+                createActivityControllerInternal(activity, fromState, transitionLength, callback);
             }
 
             @Override
@@ -230,18 +224,9 @@
         };
     }
 
-    private void createActivityControllerInternal(Launcher activity, boolean wasVisible,
-            LauncherState fromState, long transitionLength,
-            Consumer<AnimatorPlaybackController> callback) {
+    private void createActivityControllerInternal(Launcher activity, LauncherState fromState,
+            long transitionLength, Consumer<AnimatorPlaybackController> callback) {
         LauncherState endState = OVERVIEW;
-        if (wasVisible && fromState != BACKGROUND_APP) {
-            // If a translucent app was launched fom launcher, animate launcher states.
-            DeviceProfile dp = activity.getDeviceProfile();
-            long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-            callback.accept(activity.getStateManager()
-                    .createAnimationToNewWorkspace(fromState, endState, accuracy));
-            return;
-        }
         if (fromState == endState) {
             return;
         }
@@ -319,7 +304,7 @@
     @Override
     public ActivityInitListener createActivityInitListener(
             BiPredicate<Launcher, Boolean> onInitListener) {
-        return new LauncherInitListener(onInitListener);
+        return new LauncherInitListenerEx(onInitListener);
     }
 
     @Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 990bcff..44ba515 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -21,9 +21,9 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -45,6 +45,9 @@
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.R;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
@@ -62,8 +65,6 @@
 
 import java.util.function.Consumer;
 
-import androidx.annotation.UiThread;
-
 /**
  * Input consumer for handling events originating from an activity other than Launcher
  */
@@ -90,6 +91,7 @@
 
     private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
     private final MotionPauseDetector mMotionPauseDetector;
+    private final float mMotionPauseMinDisplacement;
     private VelocityTracker mVelocityTracker;
 
     private WindowTransformSwipeHandler mInteractionHandler;
@@ -107,8 +109,9 @@
     // Slop used to determine when we say that the gesture has started.
     private boolean mPassedTouchSlop;
 
-    // TODO: Start displacement should have both x and y
+    // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
     private float mStartDisplacement;
+    private float mStartDisplacementX;
 
     private Handler mMainThreadHandler;
     private Runnable mCancelRecentsAnimationRunnable = () -> {
@@ -131,6 +134,8 @@
         mMode = SysUINavigationMode.getMode(base);
 
         mMotionPauseDetector = new MotionPauseDetector(base);
+        mMotionPauseMinDisplacement = base.getResources().getDimension(
+                R.dimen.motion_pause_detector_min_displacement_from_app);
         mOnCompleteCallback = onCompleteCallback;
         mVelocityTracker = VelocityTracker.obtain();
         mInputMonitorCompat = inputMonitorCompat;
@@ -149,6 +154,7 @@
 
         mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
         mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
+
         mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture;
     }
 
@@ -218,6 +224,7 @@
                 }
                 mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
                 float displacement = getDisplacement(ev);
+                float displacementX = mLastPos.x - mDownPos.x;
 
                 if (!mPassedDragSlop) {
                     if (!mIsDeferredDownTarget) {
@@ -226,13 +233,13 @@
                         if (Math.abs(displacement) > mDragSlop) {
                             mPassedDragSlop = true;
                             mStartDisplacement = displacement;
+                            mStartDisplacementX = displacementX;
                         }
                     }
                 }
 
                 if (!mPassedTouchSlop) {
-                    if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
-                            mTouchSlop) {
+                    if (Math.hypot(displacementX, mLastPos.y - mDownPos.y) >= mTouchSlop) {
                         mPassedTouchSlop = true;
 
                         if (mIsDeferredDownTarget) {
@@ -243,6 +250,7 @@
                         if (!mPassedDragSlop) {
                             mPassedDragSlop = true;
                             mStartDisplacement = displacement;
+                            mStartDisplacementX = displacementX;
                         }
                         notifyGestureStarted();
                     }
@@ -253,12 +261,12 @@
                     mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
 
                     if (mMode == Mode.NO_BUTTON) {
-                        boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
-                        float orthogonalDisplacement = !isLandscape
-                                ? ev.getX() - mDownPos.x
-                                : ev.getY() - mDownPos.y;
-                        mMotionPauseDetector.addPosition(displacement, orthogonalDisplacement,
-                                ev.getEventTime());
+                        float horizontalDist = Math.abs(displacementX - mStartDisplacementX);
+                        float upDist = -(displacement - mStartDisplacement);
+                        boolean isLikelyToStartNewTask = horizontalDist > upDist;
+                        mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+                                || isLikelyToStartNewTask);
+                        mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
                     }
                 }
                 break;
@@ -346,7 +354,8 @@
                             : velocityY;
 
             mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
-            mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY));
+            mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
+                    mDownPos);
         } else {
             // Since we start touch tracking on DOWN, we may reach this state without actually
             // starting the gesture. In that case, just cleanup immediately.
@@ -412,7 +421,7 @@
     }
 
     @Override
-    public boolean isActive() {
+    public boolean useSharedSwipeState() {
         return mInteractionHandler != null;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index d979c99..84af109 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -36,6 +36,8 @@
  */
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
+    public static final String AIAI_PACKAGE = "com.google.android.as";
+
     /** Note that these will be shown in order from top to bottom, if available for the task. */
     private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
             new TaskSystemShortcut.AppInfo(),
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
index 6e98a5a..e3dcadc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -38,7 +38,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.InstantAppResolver;
@@ -86,7 +86,7 @@
     public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
         Task task = view.getTask();
 
-        ShortcutInfo dummyInfo = new ShortcutInfo();
+        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
         dummyInfo.intent = new Intent();
         ComponentName component = task.getTopComponent();
         dummyInfo.intent.setComponent(component);
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 6044b61..fc3f332 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -87,7 +87,7 @@
             new LooperExecutor(UiThreadHelper.getBackgroundLooper());
 
     private static final String NAVBAR_VERTICAL_SIZE = "navigation_bar_frame_height";
-    private static final String NAVBAR_HORIZONTAL_SIZE = "navigation_bar_frame_width";
+    private static final String NAVBAR_HORIZONTAL_SIZE = "navigation_bar_width";
 
     public static final EventLogArray TOUCH_INTERACTION_LOG =
             new EventLogArray("touch_interaction_log", 40);
@@ -422,7 +422,7 @@
         if (event.getAction() == ACTION_DOWN) {
             if (isInValidSystemUiState()
                     && mSwipeTouchRegion.contains(event.getX(), event.getY())) {
-                boolean useSharedState = mConsumer.isActive();
+                boolean useSharedState = mConsumer.useSharedSwipeState();
                 mConsumer.onConsumerAboutToBeSwitched();
                 mConsumer = newConsumer(useSharedState, event);
                 TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
@@ -455,13 +455,18 @@
 
         final ActivityControlHelper activityControl =
                 mOverviewComponentObserver.getActivityControlHelper();
+
         if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
             return InputConsumer.NO_OP;
         } else if (mAssistantAvailable
                 && SysUINavigationMode.INSTANCE.get(this).getMode() == Mode.NO_BUTTON
                 && AssistantTouchConsumer.withinTouchRegion(this, event)) {
-            return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
-                            ? createOtherActivityInputConsumer(event, runningTaskInfo) : null);
+
+            boolean addDelegate = !activityControl.isResumed();
+            return new AssistantTouchConsumer(this, mISystemUiProxy, addDelegate ?
+                    createOtherActivityInputConsumer(event, runningTaskInfo) : null,
+                    mInputMonitorCompat, activityControl);
+
         } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
             return OverviewInputConsumer.newInstance(activityControl, false);
         } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 5a1d387..e1b38b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -64,6 +64,9 @@
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
@@ -103,9 +106,6 @@
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.UiThread;
-
 @TargetApi(Build.VERSION_CODES.O)
 public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
         implements SwipeAnimationListener, OnApplyWindowInsetsListener {
@@ -253,6 +253,7 @@
     private boolean mGestureStarted;
     private int mLogAction = Touch.SWIPE;
     private int mLogDirection = Direction.UP;
+    private PointF mDownPos;
 
     private final RecentsAnimationWrapper mRecentsAnimationWrapper;
 
@@ -429,8 +430,19 @@
         // If we've already ended the gesture and are going home, don't prepare recents UI,
         // as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
         if (mGestureEndTarget != HOME) {
-            mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
-                    mWasLauncherAlreadyVisible, true, this::onAnimatorPlaybackControllerCreated);
+            Runnable initAnimFactory = () -> {
+                mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
+                        mWasLauncherAlreadyVisible, true,
+                        this::onAnimatorPlaybackControllerCreated);
+            };
+            if (mWasLauncherAlreadyVisible) {
+                // Launcher is visible, but might be about to stop. Thus, if we prepare recents
+                // now, it might get overridden by moveToRestState() in onStop(). To avoid this,
+                // wait until the next gesture (and possibly launcher) starts.
+                mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory);
+            } else {
+                initAnimFactory.run();
+            }
         }
         AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
 
@@ -692,9 +704,10 @@
     /**
      * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
      * @param velocity The x and y components of the velocity when the gesture ends.
+     * @param downPos The x and y value of where the gesture started.
      */
     @UiThread
-    public void onGestureEnded(float endVelocity, PointF velocity) {
+    public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
         float flingThreshold = mContext.getResources()
                 .getDimension(R.dimen.quickstep_fling_threshold_velocity);
         boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
@@ -707,6 +720,7 @@
         } else {
             mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
         }
+        mDownPos = downPos;
         handleNormalGestureEnd(endVelocity, isFling, velocity);
     }
 
@@ -733,14 +747,10 @@
         float endShift;
         final float startShift;
         Interpolator interpolator = DEACCEL;
-        int nextPage = 0;
-        int taskToLaunch = 0;
         final boolean goingToNewTask;
         if (mRecentsView != null) {
-            nextPage = mRecentsView.getNextPage();
-            final int lastTaskIndex = mRecentsView.getTaskViewCount() - 1;
             final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
-            taskToLaunch = nextPage <= lastTaskIndex ? nextPage : lastTaskIndex;
+            final int taskToLaunch = mRecentsView.getNextPage();
             goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
         } else {
             goingToNewTask = false;
@@ -806,11 +816,6 @@
             }
         }
 
-        if (mRecentsView != null && !endTarget.isLauncher && taskToLaunch != nextPage) {
-            // Scrolled to Clear all button, snap back to last task and launch it.
-            mRecentsView.snapToPage(taskToLaunch, Math.toIntExact(duration), interpolator);
-        }
-
         if (endTarget == HOME) {
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
@@ -845,6 +850,7 @@
                 : mRecentsView.getNextPage();
         UserEventDispatcher.newInstance(mContext).logStateChangeAction(
                 mLogAction, mLogDirection,
+                (int) mDownPos.x, (int) mDownPos.y,
                 ContainerType.NAVBAR, ContainerType.APP,
                 endTarget.containerType,
                 pageIndex);
@@ -1044,7 +1050,12 @@
     }
 
     private void invalidateHandlerWithLauncher() {
-        mLauncherTransitionController = null;
+        if (mLauncherTransitionController != null) {
+            if (mLauncherTransitionController.getAnimationPlayer().isStarted()) {
+                mLauncherTransitionController.getAnimationPlayer().cancel();
+            }
+            mLauncherTransitionController = null;
+        }
 
         mRecentsView.setEnableFreeScroll(true);
         mRecentsView.setRunningTaskIconScaledDown(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java
deleted file mode 100644
index 8fc89f2..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ChipsContainer.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.hints;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.view.View;
-import android.widget.FrameLayout;
-
-public class ChipsContainer extends FrameLayout {
-
-    private static final String TAG = "ChipsContainer";
-
-    public static final FloatProperty<ChipsContainer> HINT_VISIBILITY =
-            new FloatProperty<ChipsContainer>("hint_visibility") {
-                @Override
-                public void setValue(ChipsContainer chipsContainer, float v) {
-                    chipsContainer.setHintVisibility(v);
-                }
-
-                @Override
-                public Float get(ChipsContainer chipsContainer) {
-                    return chipsContainer.mHintVisibility;
-                }
-            };
-
-    private float mHintVisibility;
-
-    public ChipsContainer(Context context) {
-        super(context);
-    }
-
-    public ChipsContainer(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public void setView(View v) {
-        removeAllViews();
-        addView(v);
-    }
-
-    public void setHintVisibility(float v) {
-        if (v == 1) {
-            setVisibility(VISIBLE);
-        } else {
-            setVisibility(GONE);
-        }
-        mHintVisibility = v;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index 32d510d..71f33c3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -102,7 +102,7 @@
     public ClipAnimationHelper(Context context) {
         mWindowCornerRadius = getWindowCornerRadius(context.getResources());
         mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
-        mTaskCornerRadius = Themes.getDialogCornerRadius(context);
+        mTaskCornerRadius = TaskCornerRadius.get(context);
     }
 
     private void updateSourceStack(RemoteAnimationTargetCompat target) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
new file mode 100644
index 0000000..3ddf1b6
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+
+import android.content.Context;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+public class TaskCornerRadius {
+
+    public static float get(Context context) {
+        return supportsRoundedCornersOnWindows(context.getResources()) ?
+                Themes.getDialogCornerRadius(context):
+                context.getResources().getDimension(R.dimen.task_corner_radius_small);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index fbecd84..9db0c09 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Property;
 import android.widget.Button;
 
 import com.android.launcher3.Utilities;
@@ -26,8 +27,22 @@
 
 public class ClearAllButton extends Button implements PageCallbacks {
 
+    public static final Property<ClearAllButton, Float> VISIBILITY_ALPHA =
+            new Property<ClearAllButton, Float>(Float.class, "visibilityAlpha") {
+                @Override
+                public Float get(ClearAllButton view) {
+                    return view.mVisibilityAlpha;
+                }
+
+                @Override
+                public void set(ClearAllButton view, Float visibilityAlpha) {
+                    view.setVisibilityAlpha(visibilityAlpha);
+                }
+            };
+
     private float mScrollAlpha = 1;
     private float mContentAlpha = 1;
+    private float mVisibilityAlpha = 1;
 
     private final boolean mIsRtl;
 
@@ -58,6 +73,13 @@
         }
     }
 
+    public void setVisibilityAlpha(float alpha) {
+        if (mVisibilityAlpha != alpha) {
+            mVisibilityAlpha = alpha;
+            updateAlpha();
+        }
+    }
+
     @Override
     public void onPageScroll(ScrollState scrollState) {
         float width = getWidth();
@@ -72,7 +94,7 @@
     }
 
     private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha;
+        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
         setAlpha(alpha);
         setClickable(alpha == 1);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 446fb39..204dd56 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -32,7 +32,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -59,7 +58,6 @@
     private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
 
     private Task mTask;
-    private ImageView mImage;
     private TextView mText;
 
     public DigitalWellBeingToast(Context context, AttributeSet attrs) {
@@ -70,12 +68,15 @@
         mLauncherApps = context.getSystemService(LauncherApps.class);
     }
 
+    public TextView getTextView() {
+        return mText;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
         mText = findViewById(R.id.digital_well_being_remaining_time);
-        mImage = findViewById(R.id.digital_well_being_hourglass);
     }
 
     public void initialize(Task task, InitializeCallback callback) {
@@ -103,8 +104,6 @@
                 } else {
                     setVisibility(VISIBLE);
                     mText.setText(getText(appRemainingTimeMs));
-                    mImage.setImageResource(appRemainingTimeMs > 0 ?
-                            R.drawable.hourglass_top : R.drawable.hourglass_bottom);
                 }
 
                 callback.call(getContentDescriptionForTask(
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 1ccc3f1..d6f2235 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,9 +19,9 @@
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 
 import android.animation.AnimatorSet;
@@ -39,11 +39,10 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.PendingAnimation;
-import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.hints.ChipsContainer;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 import com.android.quickstep.util.LayoutUtils;
@@ -55,7 +54,6 @@
 public class LauncherRecentsView extends RecentsView<Launcher> {
 
     private final TransformParams mTransformParams = new TransformParams();
-    private ChipsContainer mChipsContainer;
 
     public LauncherRecentsView(Context context) {
         this(context, null);
@@ -76,14 +74,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mChipsContainer = mActivity.findViewById(R.id.hints);
-        BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) mChipsContainer.getLayoutParams();
-        params.bottomMargin = mActivity.getDeviceProfile().chipHintBottomMarginPx;
-    }
-
-    @Override
     public void setTranslationY(float translationY) {
         super.setTranslationY(translationY);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
@@ -94,16 +84,6 @@
         }
     }
 
-    public void setHintVisibility(float v) {
-        if (mChipsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
-            mChipsContainer.setHintVisibility(v);
-        }
-    }
-
-    public ChipsContainer getChipsContainer() {
-        return mChipsContainer;
-    }
-
     @Override
     public void draw(Canvas canvas) {
         maybeDrawEmptyMessage(canvas);
@@ -155,37 +135,6 @@
     }
 
     @Override
-    public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
-        PendingAnimation anim = super.createTaskLauncherAnimation(tv, duration);
-
-        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
-            anim.anim.play(ObjectAnimator.ofFloat(
-                    mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
-        }
-
-        return anim;
-    }
-
-    @Override
-    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
-            boolean shouldRemoveTask, long duration) {
-        PendingAnimation anim = super.createTaskDismissAnimation(taskView, animateTaskView,
-                shouldRemoveTask, duration);
-
-        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
-            anim.anim.play(ObjectAnimator.ofFloat(
-                    mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
-            anim.addEndListener(onEndListener -> {
-                if (!onEndListener.isSuccess) {
-                    mChipsContainer.setHintVisibility(1);
-                }
-            });
-        }
-
-        return anim;
-    }
-
-    @Override
     protected void getTaskSize(DeviceProfile dp, Rect outRect) {
         LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
     }
@@ -247,4 +196,24 @@
             }
         }
     }
+
+    @Override
+    public void reset() {
+        super.reset();
+
+        // We are moving to home or some other UI with no recents. Switch back to the home client,
+        // the home predictions should have been updated when the activity was resumed.
+        PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
+    }
+
+    @Override
+    public void setOverviewStateEnabled(boolean enabled) {
+        super.setOverviewStateEnabled(enabled);
+        if (enabled) {
+            LauncherState state = mActivity.getStateManager().getState();
+            boolean hasClearAllButton = (state.getVisibleElements(mActivity)
+                    & RECENTS_CLEAR_ALL_BUTTON) != 0;
+            setDisallowScrollToClearAll(!hasClearAllButton);
+        }
+    }
 }
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 ddb94d2..08a7616 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
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -25,9 +26,10 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 
 import android.animation.Animator;
@@ -46,6 +48,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
@@ -89,7 +92,6 @@
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
-import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
@@ -169,6 +171,7 @@
     private final ViewPool<TaskView> mTaskViewPool;
 
     private boolean mDwbToastShown;
+    private boolean mDisallowScrollToClearAll;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -322,6 +325,8 @@
         mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
         mEmptyMessagePaint.setTextSize(getResources()
                 .getDimension(R.dimen.recents_empty_message_text_size));
+        mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
+                Typeface.NORMAL));
         mEmptyMessagePadding = getResources()
                 .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
         setWillNotDraw(false);
@@ -748,8 +753,6 @@
 
         unloadVisibleTaskData();
         setCurrentPage(0);
-
-        OverviewCallbacks.get(getContext()).onResetOverview();
     }
 
     /**
@@ -1159,6 +1162,7 @@
     @SuppressWarnings("unused")
     private void dismissAllTasks(View view) {
         runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
+        mActivity.getUserEventDispatcher().logActionOnControl(TAP, CLEAR_ALL_BUTTON);
     }
 
     private void dismissCurrentTask() {
@@ -1606,4 +1610,33 @@
 
         mRecentsAnimationWrapper.finish(toRecents, onFinishComplete);
     }
+
+    public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
+        if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
+            mDisallowScrollToClearAll = disallowScrollToClearAll;
+            updateMinAndMaxScrollX();
+        }
+    }
+
+    @Override
+    protected int computeMinScrollX() {
+        if (mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the leftmost task as the min scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMinScrollX();
+    }
+
+    @Override
+    protected int computeMaxScrollX() {
+        if (!mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the rightmost task as the max scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMaxScrollX();
+    }
+
+    public ClearAllButton getClearAllButton() {
+        return mClearAllButton;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 7905230..ed68d87 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -48,6 +48,7 @@
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
+import com.android.quickstep.util.TaskCornerRadius;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -108,7 +109,7 @@
 
     public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mCornerRadius = Themes.getDialogCornerRadius(context);
+        mCornerRadius = TaskCornerRadius.get(context);
         mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 02a2ec6..3ca8b18 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -211,6 +211,10 @@
         return mMenuView;
     }
 
+    public DigitalWellBeingToast getDigitalWellBeingToast() {
+        return mDigitalWellBeingToast;
+    }
+
     /**
      * Updates this task view to the given {@param task}.
      */
diff --git a/quickstep/res/drawable/hourglass_bottom.xml b/quickstep/res/drawable/hourglass_bottom.xml
deleted file mode 100644
index b5ef008..0000000
--- a/quickstep/res/drawable/hourglass_bottom.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24"
-        android:viewportHeight="24">
-    <group>
-        <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
-        <path
-            android:fillColor="#FFFFFFFF"
-            android:pathData="M6,2V8H6l4,4L6,16H6v6H18V16h0l-4,-4,4,-4h0V2Zm6,9.5,-4,-4V4h8V7.5Z"/>
-    </group>
-</vector>
diff --git a/quickstep/res/drawable/hourglass_top.xml b/quickstep/res/drawable/ic_hourglass_top.xml
similarity index 82%
rename from quickstep/res/drawable/hourglass_top.xml
rename to quickstep/res/drawable/ic_hourglass_top.xml
index 7fc77d3..2f8bec3 100644
--- a/quickstep/res/drawable/hourglass_top.xml
+++ b/quickstep/res/drawable/ic_hourglass_top.xml
@@ -2,7 +2,8 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="?android:attr/textColorPrimary">
     <group>
         <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
         <path
diff --git a/quickstep/res/drawable/ic_pin.xml b/quickstep/res/drawable/ic_pin.xml
index 8c799e3..f9eedbc 100644
--- a/quickstep/res/drawable/ic_pin.xml
+++ b/quickstep/res/drawable/ic_pin.xml
@@ -18,9 +18,16 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="#FFffffff"
-        android:pathData="M16,12L16,4l1,0L17,2L7,2l0,2l1,0l0,8l-2,2l0,2l5.2,0l0,6l1.6,0l0,-6L18,16l0,-2L16,12z"/>
+        android:strokeColor="#FFFFFFFF"
+        android:strokeAlpha=".008"
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17 4v7l2 3v2h-6v5l-1 1-1-1v-5H5v-2l2-3V4c0-1.1 0.9 -2 2-2h6c1.11 0 2 0.89 2
+2zM9 4v7.75L7.5 14h9L15 11.75V4H9z" />
+
 </vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_split_screen.xml b/quickstep/res/drawable/ic_split_screen.xml
index 110af91..1080069 100644
--- a/quickstep/res/drawable/ic_split_screen.xml
+++ b/quickstep/res/drawable/ic_split_screen.xml
@@ -17,7 +17,8 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="?android:attr/textColorPrimary">
 
     <path
         android:fillColor="@android:color/white"
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index f96a66f..1d1c272 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -49,6 +49,7 @@
             android:layout_width="24dp"
             android:layout_height="24dp"
             android:layout_marginEnd="8dp"
+            android:src="@drawable/ic_hourglass_top"
         />
         <TextView
             android:id="@+id/digital_well_being_remaining_time"
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index a966698..95aea43 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -29,5 +29,5 @@
 
     <!-- Assistant Gesture -->
     <integer name="assistant_gesture_min_time_threshold">200</integer>
-    <integer name="assistant_gesture_corner_deg_threshold">30</integer>
+    <integer name="assistant_gesture_corner_deg_threshold">20</integer>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 6ec3bf6..32f312f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -19,6 +19,8 @@
     <dimen name="task_thumbnail_top_margin">24dp</dimen>
     <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
+    <!-- For screens without rounded corners -->
+    <dimen name="task_corner_radius_small">2dp</dimen>
 
     <dimen name="recents_page_spacing">10dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
@@ -36,7 +38,7 @@
     <dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
     <dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
     <dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
-    <dimen name="motion_pause_detector_min_displacement">48dp</dimen>
+    <dimen name="motion_pause_detector_min_displacement_from_app">36dp</dimen>
 
     <!-- Launcher app transition -->
     <dimen name="content_trans_y">50dp</dimen>
@@ -63,8 +65,8 @@
     <dimen name="shelf_surface_offset">24dp</dimen>
 
     <!-- Assistant Gestures -->
-    <dimen name="gestures_assistant_size">28dp</dimen>
-    <dimen name="gestures_assistant_drag_threshold">70dp</dimen>
+    <dimen name="gestures_assistant_size">48dp</dimen>
+    <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
 
     <!-- Distance to move elements when swiping up to go home from launcher -->
     <dimen name="home_pullback_distance">28dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 81565a5..4319b5d 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -58,4 +58,13 @@
     <!-- Annotation shown on an app card in Recents, telling that the app has a usage limit set by
     the user, and a given time is left for it today [CHAR LIMIT=22] -->
     <string name="time_left_for_app"><xliff:g id="time" example="7 minutes">%1$s</xliff:g> left today</string>
+
+    <!-- Accessibility title for the row of all-apps containing app predictions. [CHAR LIMIT=50] -->
+    <string name="title_app_suggestions">App suggestions</string>
+    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_label">All apps</string>
+    <!-- Text of the tip when user lands in all apps view for the first time, indicating where the tip toast points to is the predicted apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_prediction_tip">Your predicted apps</string>
+
+
 </resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index b263a4c..4f50cdb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -87,7 +87,7 @@
         OverviewInteractionState.INSTANCE.get(launcher)
                 .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
         if (launcher != null && launcher.getDragLayer() != null) {
-            launcher.getDragLayer().setDisallowBackGesture(shouldBackButtonBeHidden);
+            launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/OverviewCallbacks.java b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
index ef9c5c0..f5573ba 100644
--- a/quickstep/src/com/android/quickstep/OverviewCallbacks.java
+++ b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
@@ -39,7 +39,5 @@
 
     public void onInitOverviewTransition() { }
 
-    public void onResetOverview() { }
-
     public void closeAllWindows() { }
 }
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 724a492..a2f07e3 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,8 +20,8 @@
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
-import static com.android.systemui.shared.system.PackageManagerWrapper
-        .ACTION_PREFERRED_ACTIVITY_CHANGED;
+import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
+import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -29,7 +29,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ResolveInfo;
-import android.os.PatternMatcher;
 
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
@@ -109,13 +108,9 @@
                 }
 
                 mUpdateRegisteredPackage = defaultHome.getPackageName();
-                IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
-                updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
-                updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
-                updateReceiver.addDataScheme("package");
-                updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
-                        PatternMatcher.PATTERN_LITERAL);
-                mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
+                mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
+                        mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
+                        ACTION_PACKAGE_REMOVED));
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index dbae794..b67c6f8 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -16,15 +16,15 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.util.Log;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.system.QuickStepContract;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -70,8 +70,6 @@
         mContext = context;
         initializeMode();
 
-        IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
-        filter.addDataScheme("package");
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -81,7 +79,7 @@
                     dispatchModeChange();
                 }
             }
-        }, filter);
+        }, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
     }
 
     private void initializeMode() {
diff --git a/quickstep/src/com/android/quickstep/TestInformationProvider.java b/quickstep/src/com/android/quickstep/TestInformationProvider.java
index b37ddda..a948570 100644
--- a/quickstep/src/com/android/quickstep/TestInformationProvider.java
+++ b/quickstep/src/com/android/quickstep/TestInformationProvider.java
@@ -111,6 +111,14 @@
                     response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
                     break;
                 }
+
+                case TestProtocol.REQUEST_ENABLE_DRAG_LOGGING:
+                    TestProtocol.sDebugTracing = true;
+                    break;
+
+                case TestProtocol.REQUEST_DISABLE_DRAG_LOGGING:
+                    TestProtocol.sDebugTracing = false;
+                    break;
             }
             return response;
         }
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index 6dff187..4a11601 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -36,17 +36,19 @@
 @SuppressWarnings("unused")
 public class UserEventDispatcherExtension extends UserEventDispatcher {
 
+    public static final int ALL_APPS_PREDICTION_TIPS = 2;
+
     private static final String TAG = "UserEventDispatcher";
 
     public UserEventDispatcherExtension(Context context) { }
 
-    public void logStateChangeAction(int action, int dir, int srcChildTargetType,
-                                     int srcParentContainerType, int dstContainerType,
-                                     int pageIndex) {
+    public void logStateChangeAction(int action, int dir, int downX, int downY,
+                                     int srcChildTargetType, int srcParentContainerType,
+                                     int dstContainerType, int pageIndex) {
         new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
                 dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
-        super.logStateChangeAction(action, dir, srcChildTargetType, srcParentContainerType,
-                dstContainerType, pageIndex);
+        super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
+                srcParentContainerType, dstContainerType, pageIndex);
     }
 
     public void logActionTip(int actionType, int viewType) {
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 47f4f4d..c8aed81 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -43,8 +43,6 @@
         float extraSpace;
         if (dp.isVerticalBarLayout()) {
             extraSpace = 0;
-        } else if (FeatureFlags.ENABLE_HINTS_IN_OVERVIEW.get()){
-            extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx + dp.chipHintHeightPx;
         } else {
             extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx;
         }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index ae5f390..f58f0d4 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -38,28 +38,26 @@
     private final float mSpeedVerySlow;
     private final float mSpeedSomewhatFast;
     private final float mSpeedFast;
-    private final float mMinDisplacementForPause;
     private final Alarm mForcePauseTimeout;
 
     private Long mPreviousTime = null;
     private Float mPreviousPosition = null;
     private Float mPreviousVelocity = null;
 
-    private TotalDisplacement mTotalDisplacement = new TotalDisplacement();
     private Float mFirstPosition = null;
-    private Float mFirstOrthogonalPosition = null;
 
     private OnMotionPauseListener mOnMotionPauseListener;
     private boolean mIsPaused;
     // Bias more for the first pause to make it feel extra responsive.
     private boolean mHasEverBeenPaused;
+    /** @see #setDisallowPause(boolean) */
+    private boolean mDisallowPause;
 
     public MotionPauseDetector(Context context) {
         Resources res = context.getResources();
         mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
-        mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
     }
@@ -73,28 +71,30 @@
     }
 
     /**
+     * @param disallowPause If true, we will not detect any pauses until this is set to false again.
+     */
+    public void setDisallowPause(boolean disallowPause) {
+        mDisallowPause = disallowPause;
+        updatePaused(mIsPaused);
+    }
+
+    /**
      * Computes velocity and acceleration to determine whether the motion is paused.
      * @param position The x or y component of the motion being tracked.
-     * @param orthogonalPosition The x or y component (opposite of {@param position}) of the motion.
      *
      * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
      */
-    public void addPosition(float position, float orthogonalPosition, long time) {
+    public void addPosition(float position, long time) {
         if (mFirstPosition == null) {
             mFirstPosition = position;
         }
-        if (mFirstOrthogonalPosition == null) {
-            mFirstOrthogonalPosition = orthogonalPosition;
-        }
         mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
         if (mPreviousTime != null && mPreviousPosition != null) {
             long changeInTime = Math.max(1, time - mPreviousTime);
             float changeInPosition = position - mPreviousPosition;
             float velocity = changeInPosition / changeInTime;
             if (mPreviousVelocity != null) {
-                mTotalDisplacement.set(Math.abs(position - mFirstPosition),
-                        Math.abs(orthogonalPosition - mFirstOrthogonalPosition));
-                checkMotionPaused(velocity, mPreviousVelocity, mTotalDisplacement);
+                checkMotionPaused(velocity, mPreviousVelocity);
             }
             mPreviousVelocity = velocity;
         }
@@ -102,8 +102,7 @@
         mPreviousPosition = position;
     }
 
-    private void checkMotionPaused(float velocity, float prevVelocity,
-            TotalDisplacement totalDisplacement) {
+    private void checkMotionPaused(float velocity, float prevVelocity) {
         float speed = Math.abs(velocity);
         float previousSpeed = Math.abs(prevVelocity);
         boolean isPaused;
@@ -125,16 +124,13 @@
                 }
             }
         }
-        boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
-        boolean isDisplacementOrthogonal = totalDisplacement.orthogonal > totalDisplacement.primary;
-        if (!passedMinDisplacement || isDisplacementOrthogonal) {
-            mForcePauseTimeout.cancelAlarm();
-            isPaused = false;
-        }
         updatePaused(isPaused);
     }
 
     private void updatePaused(boolean isPaused) {
+        if (mDisallowPause) {
+            isPaused = false;
+        }
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
             if (mIsPaused) {
@@ -151,8 +147,6 @@
         mPreviousPosition = null;
         mPreviousVelocity = null;
         mFirstPosition = null;
-        mFirstOrthogonalPosition = null;
-        mTotalDisplacement.set(0, 0);
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
         mForcePauseTimeout.cancelAlarm();
@@ -165,18 +159,4 @@
     public interface OnMotionPauseListener {
         void onMotionPauseChanged(boolean isPaused);
     }
-
-    /**
-     * Contains the displacement from the first tracked position,
-     * along both the primary and orthogonal axes.
-     */
-    private class TotalDisplacement {
-        public float primary;
-        public float orthogonal;
-
-        public void set(float primaryDisplacement, float orthogonalDisplacement) {
-            this.primary = primaryDisplacement;
-            this.orthogonal = orthogonalDisplacement;
-        }
-    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
new file mode 100644
index 0000000..43f6039
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -0,0 +1,168 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.quickstep;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.model.AppLaunchTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AppPredictionsUITests  extends AbstractQuickStepTest {
+    private static final int DEFAULT_APP_LAUNCH_TIMES = 3;
+    private static final String TAG = "AppPredictionsUITests";
+
+    private LauncherActivityInfo mSampleApp1;
+    private LauncherActivityInfo mSampleApp2;
+    private LauncherActivityInfo mSampleApp3;
+
+    private AppPredictor.Callback mCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList(null, Process.myUserHandle());
+        mSampleApp1 = activities.get(0);
+        mSampleApp2 = activities.get(1);
+        mSampleApp3 = activities.get(2);
+
+        // Disable app tracker
+        AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
+
+        mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback(
+                Client.HOME);
+
+        mDevice.setOrientationNatural();
+    }
+
+    @After
+    public void tearDown() throws Throwable {
+        mDevice.unfreezeRotation();
+    }
+
+    /**
+     * Test that prediction UI is updated as soon as we get predictions from the system
+     */
+    @Test
+    public void testPredictionExistsInAllApps() {
+        mActivityMonitor.startLauncher();
+        mLauncher.pressHome().switchToAllApps();
+
+        // There has not been any update, verify that progress bar is showing
+        waitForLauncherCondition("Prediction is not in loading state", launcher -> {
+            ProgressBar p = findLoadingBar(launcher);
+            return p != null && p.isShown();
+        });
+
+        // Dispatch an update
+        sendPredictionUpdate(mSampleApp1, mSampleApp2);
+        waitForLauncherCondition("Predictions were not updated in loading state",
+                launcher -> getPredictedApp(launcher).size() == 2);
+    }
+
+    /**
+     * Test tat prediction update is deferred if it is already visible
+     */
+    @Test
+    public void testPredictionsDeferredUntilHome() {
+        mActivityMonitor.startLauncher();
+        sendPredictionUpdate(mSampleApp1, mSampleApp2);
+        mLauncher.pressHome().switchToAllApps();
+        waitForLauncherCondition("Predictions were not updated in loading state",
+                launcher -> getPredictedApp(launcher).size() == 2);
+
+        // Update predictions while all-apps is visible
+        sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3);
+        assertEquals(2, getFromLauncher(this::getPredictedApp).size());
+
+        // Go home and go back to all-apps
+        mLauncher.pressHome().switchToAllApps();
+        assertEquals(3, getFromLauncher(this::getPredictedApp).size());
+    }
+
+    public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
+        PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
+                .findFixedRowByType(PredictionRowView.class);
+
+        ArrayList<BubbleTextView> predictedAppViews = new ArrayList<>();
+        for (int i = 0; i < container.getChildCount(); i++) {
+            View view = container.getChildAt(i);
+            if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) {
+                predictedAppViews.add((BubbleTextView) view);
+            }
+        }
+        return predictedAppViews;
+    }
+
+    private ProgressBar findLoadingBar(Launcher launcher) {
+        PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
+                .findFixedRowByType(PredictionRowView.class);
+
+        for (int i = 0; i < container.getChildCount(); i++) {
+            View view = container.getChildAt(i);
+            if (view instanceof ProgressBar) {
+                return (ProgressBar) view;
+            }
+        }
+        return null;
+    }
+
+
+    private void sendPredictionUpdate(LauncherActivityInfo... activities) {
+        getOnUiThread(() -> {
+            List<AppTarget> targets = new ArrayList<>(activities.length);
+            for (LauncherActivityInfo info : activities) {
+                ComponentName cn = info.getComponentName();
+                AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn))
+                        .setTarget(cn.getPackageName(), info.getUser())
+                        .setClassName(cn.getClassName())
+                        .build();
+                targets.add(target);
+            }
+            mCallback.onTargetsAvailable(targets);
+            return null;
+        });
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
new file mode 100644
index 0000000..8798157
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -0,0 +1,87 @@
+package com.android.quickstep;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.PendingIntent;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+
+import com.android.launcher3.Launcher;
+import com.android.quickstep.views.DigitalWellBeingToast;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+import org.junit.Test;
+
+import java.time.Duration;
+
+public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
+    private static final String CALCULATOR_PACKAGE =
+            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+    @Test
+    public void testToast() throws Exception {
+        final UsageStatsManager usageStatsManager =
+                mTargetContext.getSystemService(UsageStatsManager.class);
+        final int observerId = 0;
+
+        try {
+            final String[] packages = new String[]{CALCULATOR_PACKAGE};
+
+            // Set time limit for app.
+            runWithShellPermission(() ->
+                    usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
+                            Duration.ofSeconds(600), Duration.ofSeconds(300),
+                            PendingIntent.getActivity(mTargetContext, -1, new Intent(), 0)));
+
+            mLauncher.pressHome();
+            final DigitalWellBeingToast toast = getToast();
+
+            assertTrue("Toast is not visible", toast.isShown());
+            assertEquals("Toast text: ", "5 minutes left today", toast.getTextView().getText());
+
+            // Unset time limit for app.
+            runWithShellPermission(
+                    () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
+
+            mLauncher.pressHome();
+            assertFalse("Toast is visible", getToast().isShown());
+        } finally {
+            runWithShellPermission(
+                    () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
+        }
+    }
+
+    private DigitalWellBeingToast getToast() {
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
+        waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
+        waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
+
+        return getFromLauncher(launcher -> {
+            final TaskView task = getLatestTask(launcher);
+            assertTrue("Latest task is not Calculator",
+                    CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
+            return task.getDigitalWellBeingToast();
+        });
+    }
+
+    private TaskView getLatestTask(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+    }
+
+    private void runWithShellPermission(Runnable action) {
+        getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            action.run();
+        } finally {
+            getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+        }
+
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 6034791..e552f56 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -21,6 +21,7 @@
 import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
 import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
 import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
@@ -85,9 +86,9 @@
                     final LauncherInstrumentation.NavigationModel originalMode =
                             mLauncher.getNavigationModel();
                     try {
-//                        if (mode == ZERO_BUTTON || mode == ALL) {
-//                            evaluateWithZeroButtons();
-//                        }
+                        if (mode == ZERO_BUTTON || mode == ALL) {
+                            evaluateWithZeroButtons();
+                        }
                         if (mode == TWO_BUTTON || mode == ALL) {
                             evaluateWithTwoButtons();
                         }
@@ -131,7 +132,10 @@
                             overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
 
                     for (int i = 0; i != 100; ++i) {
-                        if (mLauncher.getNavigationModel() == expectedMode) return;
+                        if (mLauncher.getNavigationModel() == expectedMode) {
+                            Thread.sleep(1000);
+                            return;
+                        }
                         Thread.sleep(100);
                     }
                     Assert.fail("Couldn't switch to " + overlayPackage);
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index dc83e87..a47c8e7 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -61,7 +61,7 @@
     }
 
     @Test
-    @Ignore // Ignoring until gestural navigation event sequence settles
+    @Ignore // Ignoring until race condition repro framework is changes for multi-process case.
     @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
     public void testPressHome() {
         runTest(enterEvt(Launcher.ON_CREATE_EVT),
@@ -76,14 +76,6 @@
     }
 
     @Test
-    @Ignore // Ignoring until gestural navigation event sequence settles
-    @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
-    public void testSwipeToOverview() {
-        closeLauncherActivity();
-        mLauncher.getBackground().switchToOverview();
-    }
-
-    @Test
     @NavigationModeSwitch
     public void testStressPressHome() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 1709516..b0ce5f5 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -60,7 +62,7 @@
     }
 
     private void startTestApps() throws Exception {
-        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_BROWSER));
+        startAppFast(getAppPackageName());
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
 
@@ -72,7 +74,7 @@
 
     @Test
     @PortraitLandscape
-    @Ignore
+    @Ignore // Enable after b/131115533
     public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
         mDevice.pressRecentApps();
         waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
diff --git a/res/drawable/ic_close.xml b/res/drawable/ic_close.xml
deleted file mode 100644
index 8b2f55f..0000000
--- a/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!-- Copyright (C) 2017 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_corp.xml b/res/drawable/ic_corp.xml
index 48f5007..b59113d 100644
--- a/res/drawable/ic_corp.xml
+++ b/res/drawable/ic_corp.xml
@@ -17,8 +17,11 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/textColorHint" >
     <path
-        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
-        android:fillColor="?android:attr/textColorHint"/>
+        android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99
+2L2 19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zM10
+4h4v2h-4V4zm10 15H4V8h16v11z"
+        android:fillColor="@android:color/white"/>
 </vector>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/ic_drag_handle.xml
similarity index 82%
rename from res/drawable/deep_shortcuts_drag_handle.xml
rename to res/drawable/ic_drag_handle.xml
index 8fc3779..0181ff1 100644
--- a/res/drawable/deep_shortcuts_drag_handle.xml
+++ b/res/drawable/ic_drag_handle.xml
@@ -22,7 +22,6 @@
         android:tint="?android:attr/textColorHint" >
 
     <path
-        android:pathData="M19,9H5c-0.55,0-1,0.45-1,1l0,0c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1l0,0C20,9.45,19.55,9,19,9z M5,
-        15h14c0.55,0,1-0.45,1-1l0,0c0-0.55-0.45-1-1-1H5c-0.55,0-1,0.45-1,1l0,0C4,14.55,4.45,15,5,15z"
+        android:pathData="M20,9H4v2h16V9z M4,15h16v-2H4V15z"
         android:fillColor="@android:color/white" />
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_info_no_shadow.xml b/res/drawable/ic_info_no_shadow.xml
index d816f12..7c43779 100644
--- a/res/drawable/ic_info_no_shadow.xml
+++ b/res/drawable/ic_info_no_shadow.xml
@@ -21,12 +21,12 @@
         android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 11 7 H 13 V 9 H 11 V 7 Z" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 11 11 H 13 V 17 H 11 V 11 Z" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,20c-4.41,0-8-3.59-8-8 c0-4.41,3.59-8,8-8s8,3.59,8,8C20,16.41,16.41,20,12,20z" />
 </vector>
diff --git a/res/drawable/ic_install_no_shadow.xml b/res/drawable/ic_install_no_shadow.xml
index ffce22a..eaad0de 100644
--- a/res/drawable/ic_install_no_shadow.xml
+++ b/res/drawable/ic_install_no_shadow.xml
@@ -17,11 +17,13 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="?android:attr/textColorPrimary"
-        android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+        android:fillColor="@android:color/white"
+        android:pathData="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z" />
     <path
-        android:pathData="M0 0h24v24H0z" />
+        android:fillColor="@android:color/white"
+        android:pathData="M 17 11.5 L 15.59 10.09 L 13 12.67 L 13 4 L 11 4 L 11 12.67 L 8.41 10.09 L 7 11.5 L 12 16.5 Z" />
 </vector>
diff --git a/res/drawable/ic_palette.xml b/res/drawable/ic_palette.xml
new file mode 100644
index 0000000..21cec2d
--- /dev/null
+++ b/res/drawable/ic_palette.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:strokeWidth="1"
+        android:pathData="M10,20 C4.49,20 0,15.51 0,10 C0,4.49 4.49,0 10,0 C15.51,0 20,4.04 20,9
+            C20,12.31 17.31,15 14,15 L12.23,15 C11.95,15 11.73,15.22 11.73,15.5 C11.73,15.62
+            11.78,15.73 11.86,15.83 C12.27,16.3 12.5,16.89 12.5,17.5 C12.5,18.88 11.38,20
+            10,20 Z M10,2 C5.59,2 2,5.59 2,10 C2,14.41 5.59,18 10,18 C10.28,18 10.5,17.78
+            10.5,17.5 C10.5,17.34 10.42,17.22 10.36,17.15 C9.95,16.69 9.73,16.1 9.73,15.5
+            C9.73,14.12 10.85,13 12.23,13 L14,13 C16.21,13 18,11.21 18,9 C18,5.14 14.41,2
+            10,2 Z M4.5,11 C5.32842712,11 6,10.3284271 6,9.5 C6,8.67157288 5.32842712,8
+            4.5,8 C3.67157288,8 3,8.67157288 3,9.5 C3,10.3284271 3.67157288,11 4.5,11 Z
+            M7.5,7 C8.32842712,7 9,6.32842712 9,5.5 C9,4.67157288 8.32842712,4 7.5,4
+            C6.67157288,4 6,4.67157288 6,5.5 C6,6.32842712 6.67157288,7 7.5,7 Z M12.5,7
+            C13.3284271,7 14,6.32842712 14,5.5 C14,4.67157288 13.3284271,4 12.5,4
+            C11.6715729,4 11,4.67157288 11,5.5 C11,6.32842712 11.6715729,7 12.5,7 Z M15.5,11
+            C16.3284271,11 17,10.3284271 17,9.5 C17,8.67157288 16.3284271,8 15.5,8
+            C14.6715729,8 14,8.67157288 14,9.5 C14,10.3284271 14.6715729,11 15.5,11 Z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_remove_no_shadow.xml b/res/drawable/ic_remove_no_shadow.xml
index be7f9f3..2c706db 100644
--- a/res/drawable/ic_remove_no_shadow.xml
+++ b/res/drawable/ic_remove_no_shadow.xml
@@ -14,14 +14,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/textColorPrimary" >
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M13.41,12l5.29-5.29c0.39-0.39,0.39-1.02,0-1.41c-0.39-0.39-1.02-0.39-1.41,0L12,10.59L6.71,
-        5.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41L10.59,12l-5.29,5.29c-0.39,0.39-0.39,1.02,
-        0,1.41c0.39,0.39,1.02,0.39,1.41,0L12,13.41l5.29,5.29c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L13.41,12z"/>
+        android:fillColor="@android:color/white"
+        android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z" />
 </vector>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index a83aab3..8a3728c 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -20,7 +20,7 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M13.85,22.25h-3.7c-0.74,0-1.36-0.54-1.45-1.27l-0.27-1.89c-0.27-0.14-0.53-0.29-0.79-0.46l-1.8,0.72
             c-0.7,0.26-1.47-0.03-1.81-0.65L2.2,15.53c-0.35-0.66-0.2-1.44,0.36-1.88l1.53-1.19c-0.01-0.15-0.02-0.3-0.02-0.46
             c0-0.15,0.01-0.31,0.02-0.46l-1.52-1.19C1.98,9.9,1.83,9.09,2.2,8.47l1.85-3.19c0.34-0.62,1.11-0.9,1.79-0.63l1.81,0.73
@@ -38,6 +38,6 @@
              M18.22,6.27c0,0.01,0.01,0.02,0.01,0.02L18.22,6.27z M5.79,6.25L5.78,6.27C5.78,6.27,5.79,6.26,5.79,6.25z M13.31,3.28
             c0,0.01,0,0.01,0,0.02L13.31,3.28z M10.69,3.26l0,0.02C10.69,3.27,10.69,3.27,10.69,3.26z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M8.5,12a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"/>
 </vector>
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index 37632d1..6aff102 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -20,12 +20,12 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4H15z M17,19H7V6h10V19z" />
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 9 8 H 11 V 17 H 9 V 8 Z" />
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 13 8 H 15 V 17 H 13 V 8 Z" />
 </vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index 7fd9340..9543f88 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2016 The Android Open Source Project
+   Copyright (C) 2016 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.
@@ -17,9 +17,10 @@
         android:width="@dimen/options_menu_icon_size"
         android:height="@dimen/options_menu_icon_size"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="?android:attr/textColorPrimary"
+        android:fillColor="@android:color/white"
         android:pathData="M9,12.71l2.14,2.58l3-3.87L18,16.57H6L9,12.71z M5,5h6V3H5C3.9,3,3,3.9,3,5v6h2V5z M19,19h-6v2h6c1.1,0,2-0.9,2-2v-6h-2V19z
             M5,19v-6H3v6c0,1.1,0.9,2,2,2h6v-2H5z M19,5v6h2V5c0-1.1-0.9-2-2-2h-6v2H19z M16,9c0.55,0,1-0.45,1-1s-0.45-1-1-1
             c-0.55,0-1,0.45-1,1S15.45,9,16,9z"/>
diff --git a/res/drawable/ic_warning.xml b/res/drawable/ic_warning.xml
index 332563d..c2c6e98 100644
--- a/res/drawable/ic_warning.xml
+++ b/res/drawable/ic_warning.xml
@@ -20,8 +20,12 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M22.85,19.39L12.93,2.25c-0.41-0.71-1.44-0.71-1.85,0L1.15,19.39C0.73,20.11,1.25,21,2.07,21h19.85
-        C22.75,21,23.27,20.11,22.85,19.39z M11,10c0-0.55,0.45-1,1-1s1,0.45,1,1v3c0,0.55-0.45,1-1,1s-1-0.45-1-1V10z M12,18.2
-        c-0.61,0-1.1-0.49-1.1-1.1S11.39,16,12,16s1.1,0.49,1.1,1.1C13.1,17.71,12.61,18.2,12,18.2z"/>
+        android:fillColor="@android:color/white"
+        android:pathData="M12,5.99L19.53,19H4.47L12,5.99 M12,2L1,21h22L12,2L12,2z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 13 16 L 11 16 L 11 18 L 13 18 L 13 16 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 13 10 L 11 10 L 11 14 L 13 14 L 13 10 Z" />
 </vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 3ebbb68..460fe94 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -17,9 +17,10 @@
         android:width="@dimen/options_menu_icon_size"
         android:height="@dimen/options_menu_icon_size"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="?android:attr/textColorPrimary"
+        android:fillColor="@android:color/white"
         android:pathData="M16.66,4.52l2.83,2.83l-2.83,2.83l-2.83-2.83L16.66,4.52 M9,5v4H5V5H9 M19,15v4h-4v-4H19 M9,15v4H5v-4H9 M16.66,1.69
             L11,7.34L16.66,13l5.66-5.66L16.66,1.69L16.66,1.69z M11,3H3v8h8V7.34V3L11,3z M21,13h-4.34H13v8h8V13L21,13z M11,13H3v8h8V13L11,13z"/>
 </vector>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 33ff46b..9427ae0 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -24,7 +24,7 @@
     android:clipChildren="true"
     android:clipToPadding="false"
     android:focusable="false"
-    android:saveEnabled="false" >
+    android:saveEnabled="false">
 
     <include
         layout="@layout/all_apps_rv_layout"
@@ -47,7 +47,8 @@
             android:layout_height="@dimen/all_apps_header_tab_height"
             android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
             android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
-            android:orientation="horizontal">
+            android:orientation="horizontal"
+            style="@style/TextHeadline">
 
             <Button
                 android:id="@+id/tab_personal"
@@ -55,7 +56,6 @@
                 android:layout_height="match_parent"
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
-                android:fontFamily="sans-serif-medium"
                 android:text="@string/all_apps_personal_tab"
                 android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
@@ -67,7 +67,6 @@
                 android:layout_height="match_parent"
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
-                android:fontFamily="sans-serif-medium"
                 android:text="@string/all_apps_work_tab"
                 android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 92f70e6..840a8b7 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -29,11 +29,10 @@
         android:textAlignment="viewStart"
         android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
         android:paddingEnd="@dimen/popup_padding_end"
-        android:drawableEnd="@drawable/deep_shortcuts_drag_handle"
+        android:drawableEnd="@drawable/ic_drag_handle"
         android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
         android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="sans-serif"
         launcher:layoutHorizontal="true"
         launcher:iconDisplay="shortcut_popup"
         launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index c9cea80..6ecc1f5 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -52,11 +52,6 @@
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
-        <include
-            android:id="@+id/hints"
-            layout="@layout/hint_container"
-            android:visibility="gone"/>
-
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 04f3d02..4b7097a 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -31,7 +31,6 @@
         android:paddingEnd="@dimen/popup_padding_end"
         android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="sans-serif"
         launcher:iconDisplay="shortcut_popup"
         launcher:layoutHorizontal="true"
         android:focusable="false" />
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index c77b0b9..64f2362 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -34,7 +34,6 @@
             android:layout_weight="1"
             android:ellipsize="end"
             android:fadingEdge="horizontal"
-            android:fontFamily="sans-serif-condensed"
             android:gravity="start"
             android:singleLine="true"
             android:maxLines="1"
@@ -51,7 +50,6 @@
             android:layout_marginLeft="5dp"
             android:textColor="?android:attr/textColorSecondary"
             android:textSize="14sp"
-            android:fontFamily="sans-serif-condensed"
             android:alpha="0.8" />
     </LinearLayout>
 
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index 6bf9048..3fdfc96 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -26,7 +26,7 @@
     android:theme="?attr/widgetsTheme">
 
     <TextView
-        style="@style/TextTitle"
+        style="@style/TextHeadline"
         android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/res/layout/widgets_scroll_container.xml b/res/layout/widgets_scroll_container.xml
index 33c981a..fc509d1 100644
--- a/res/layout/widgets_scroll_container.xml
+++ b/res/layout/widgets_scroll_container.xml
@@ -23,6 +23,7 @@
     android:scrollbars="none">
     <LinearLayout
         android:id="@+id/widgets_cell_list"
+        style="@style/TextTitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingStart="0dp"
diff --git a/res/layout/work_tab_bottom_user_education_view.xml b/res/layout/work_tab_bottom_user_education_view.xml
index ba6a939..ac2deeb 100644
--- a/res/layout/work_tab_bottom_user_education_view.xml
+++ b/res/layout/work_tab_bottom_user_education_view.xml
@@ -44,7 +44,7 @@
         android:layout_marginEnd="12dp"
         android:layout_gravity="right"
         android:contentDescription="@string/bottom_work_tab_user_education_close_button"
-        android:src="@drawable/ic_close"/>
+        android:src="@drawable/ic_remove_no_shadow"/>
 
     <TextView
         android:layout_width="wrap_content"
diff --git a/res/values/config.xml b/res/values/config.xml
index 4b68b50..83aea8b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -76,7 +76,7 @@
     <string name="wallpaper_picker_package" translatable="false"></string>
 
     <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
-    <string name="shortcutinfocompat_badgepkg_whitelist" translatable="false"></string>
+    <string name="shortcutinfo_badgepkg_whitelist" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ec63e35..9b84cc9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -75,8 +75,8 @@
         <item name="allAppsScrimColor">#EA212121</item>
         <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
-        <item name="popupColorPrimary">?android:attr/colorPrimary</item>
-        <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
+        <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
+        <item name="popupColorSecondary">#5F6368</item> <!-- Gray 700 -->
         <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">#FF464646</item>
@@ -126,7 +126,7 @@
 
     <style name="WidgetContainerTheme.Dark" />
 
-    <style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault">
+    <style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:minWidth">@dimen/fastscroll_popup_width</item>
         <item name="android:layout_height">@dimen/fastscroll_popup_height</item>
@@ -173,7 +173,7 @@
     </style>
 
     <!-- Drop targets -->
-    <style name="DropTargetButtonBase">
+    <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:drawablePadding">7.5dp</item>
         <item name="android:paddingLeft">16dp</item>
         <item name="android:paddingRight">16dp</item>
@@ -189,6 +189,8 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
+    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
+
     <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
 
     <style name="AllAppsEmptySearchBackground">
diff --git a/robolectric_tests/resources/cache_data_updated_task_data.txt b/robolectric_tests/resources/cache_data_updated_task_data.txt
index 8199687..302d58f 100644
--- a/robolectric_tests/resources/cache_data_updated_task_data.txt
+++ b/robolectric_tests/resources/cache_data_updated_task_data.txt
@@ -1,6 +1,6 @@
 # Model data used by CacheDataUpdatedTaskTest
 
-classMap s com.android.launcher3.ShortcutInfo
+classMap s com.android.launcher3.WorkspaceItemInfo
 
 # Items for the BgDataModel
 
diff --git a/robolectric_tests/resources/package_install_state_change_task_data.txt b/robolectric_tests/resources/package_install_state_change_task_data.txt
index 84f9c16..4d63664 100644
--- a/robolectric_tests/resources/package_install_state_change_task_data.txt
+++ b/robolectric_tests/resources/package_install_state_change_task_data.txt
@@ -1,6 +1,6 @@
 # Model data used by PackageInstallStateChangeTaskTest
 
-classMap s com.android.launcher3.ShortcutInfo
+classMap s com.android.launcher3.WorkspaceItemInfo
 classMap w com.android.launcher3.LauncherAppWidgetInfo
 
 # Items for the BgDataModel
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
index ae81ff2..1351348 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -22,7 +22,6 @@
     public final FlagOverrideRule flags = new FlagOverrideRule();
 
     @FlagOverride(key = "EXAMPLE_FLAG", value = true)
-    @FlagOverride(key = "QUICK_SWITCH", value = false)
     @Test
     public void withFlagOn() {
         assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
diff --git a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 4f49817..d7a2278 100644
--- a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -7,19 +7,14 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
-import android.net.Uri;
 import android.util.Pair;
 
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
@@ -103,7 +98,7 @@
 
     @Test
     public void testAddItem_existing_item_ignored() throws Exception {
-        ShortcutInfo info = new ShortcutInfo();
+        WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.intent = new Intent().setComponent(mComponent1);
 
         // Setup a screen with a hole
@@ -115,10 +110,10 @@
 
     @Test
     public void testAddItem_some_items_added() throws Exception {
-        ShortcutInfo info = new ShortcutInfo();
+        WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.intent = new Intent().setComponent(mComponent1);
 
-        ShortcutInfo info2 = new ShortcutInfo();
+        WorkspaceItemInfo info2 = new WorkspaceItemInfo();
         info2.intent = new Intent().setComponent(mComponent2);
 
         // Setup a screen with a hole
@@ -155,7 +150,7 @@
                     continue;
                 }
 
-                ShortcutInfo info = new ShortcutInfo();
+                WorkspaceItemInfo info = new WorkspaceItemInfo();
                 info.intent = new Intent().setComponent(mComponent1);
                 info.id = startId++;
                 info.screenId = screenId;
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 2334993..42848f4 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -8,7 +8,7 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -41,7 +41,7 @@
     }
 
     @Test
-    @Ignore("This test fails with resource errors")
+    @Ignore("This test fails with resource errors") // b/131115553
     public void testCacheUpdate_update_apps() throws Exception {
         // Clear all icons from apps list so that its easy to check what was updated
         for (AppInfo info : allAppsList.data) {
@@ -66,7 +66,7 @@
     }
 
     @Test
-    @Ignore("This test fails with resource errors")
+    @Ignore("This test fails with resource errors") // b/131115553
     public void testSessionUpdate_ignores_normal_apps() throws Exception {
         executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
 
@@ -75,7 +75,7 @@
     }
 
     @Test
-    @Ignore("This test fails with resource errors")
+    @Ignore("This test fails with resource errors") // b/131115553
     public void testSessionUpdate_updates_pending_apps() throws Exception {
         executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
 
@@ -89,10 +89,10 @@
         for (ItemInfo info : bgDataModel.itemsIdMap) {
             if (updates.contains(info.id)) {
                 assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
-                assertNotNull(((ShortcutInfo) info).iconBitmap);
+                assertNotNull(((WorkspaceItemInfo) info).iconBitmap);
             } else {
                 assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
-                assertNull(((ShortcutInfo) info).iconBitmap);
+                assertNull(((WorkspaceItemInfo) info).iconBitmap);
             }
         }
     }
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ce07a27..53f6a06 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -10,8 +10,8 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.FlagOverrideRule;
-import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
 import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
 import com.android.launcher3.util.IntArray;
 
@@ -210,9 +210,11 @@
         }});
     }
 
-    @FlagOverride(key = "QSB_ON_FIRST_SCREEN", value = true)
     @Test
     public void testWorkspace_first_row_blocked() throws Exception {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
         // The first screen has one item on the 4th column which needs moving, as the first row
         // will be kept empty.
         int[][][] ids = createGrid(new int[][][]{{
@@ -236,9 +238,11 @@
         }});
     }
 
-    @FlagOverride(key = "QSB_ON_FIRST_SCREEN", value = true)
     @Test
     public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
         // Items will get moved to the next screen to keep the first screen empty.
         int[][][] ids = createGrid(new int[][][]{{
                 { -1, -1, -1, -1},
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index c7b4613..e9324f9 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -4,7 +4,7 @@
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 
@@ -58,9 +58,9 @@
     private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
         HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
         for (ItemInfo info : bgDataModel.itemsIdMap) {
-            if (info instanceof ShortcutInfo) {
+            if (info instanceof WorkspaceItemInfo) {
                 assertEquals(updates.contains(info.id) ? progress: 0,
-                        ((ShortcutInfo) info).getInstallProgress());
+                        ((WorkspaceItemInfo) info).getInstallProgress());
             } else {
                 assertEquals(updates.contains(info.id) ? progress: -1,
                         ((LauncherAppWidgetInfo) info).installProgress);
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
similarity index 75%
rename from tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
rename to robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index d224c89..83bf7da 100644
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -22,16 +22,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 
 import android.content.pm.ShortcutInfo;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,8 +39,7 @@
 /**
  * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
  */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
 public class PopupPopulatorTest {
 
     @Test
@@ -81,20 +79,20 @@
     }
 
     private void filterShortcutsAndAssertNumStaticAndDynamic(
-            List<ShortcutInfoCompat> shortcuts, int expectedStatic, int expectedDynamic) {
+            List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) {
         filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
     }
 
-    private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfoCompat> shortcuts,
+    private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts,
             int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
         Collections.shuffle(shortcuts);
-        List<ShortcutInfoCompat> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
+        List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
                 shortcuts, shortcutIdToRemove);
         assertIsSorted(filteredShortcuts);
 
         int numStatic = 0;
         int numDynamic = 0;
-        for (ShortcutInfoCompat shortcut : filteredShortcuts) {
+        for (ShortcutInfo shortcut : filteredShortcuts) {
             if (shortcut.isDeclaredInManifest()) {
                 numStatic++;
             }
@@ -106,11 +104,11 @@
         assertEquals(expectedDynamic, numDynamic);
     }
 
-    private void assertIsSorted(List<ShortcutInfoCompat> shortcuts) {
+    private void assertIsSorted(List<ShortcutInfo> shortcuts) {
         int lastStaticRank = -1;
         int lastDynamicRank = -1;
         boolean hasSeenDynamic = false;
-        for (ShortcutInfoCompat shortcut : shortcuts) {
+        for (ShortcutInfo shortcut : shortcuts) {
             int rank = shortcut.getRank();
             if (shortcut.isDeclaredInManifest()) {
                 assertFalse("Static shortcuts should come before all dynamic shortcuts.",
@@ -126,51 +124,24 @@
         }
     }
 
-    private List<ShortcutInfoCompat> createShortcutsList(int numStatic, int numDynamic) {
-        List<ShortcutInfoCompat> shortcuts = new ArrayList<>();
+    private List<ShortcutInfo> createShortcutsList(int numStatic, int numDynamic) {
+        List<ShortcutInfo> shortcuts = new ArrayList<>();
         for (int i = 0; i < numStatic; i++) {
-            shortcuts.add(new Shortcut(true, i));
+            shortcuts.add(createInfo(true, i));
         }
         for (int i = 0; i < numDynamic; i++) {
-            shortcuts.add(new Shortcut(false, i));
+            shortcuts.add(createInfo(false, i));
         }
         return shortcuts;
     }
 
-    private class Shortcut extends ShortcutInfoCompat {
-        private boolean mIsStatic;
-        private int mRank;
-        private String mId;
-
-        public Shortcut(ShortcutInfo shortcutInfo) {
-            super(shortcutInfo);
-        }
-
-        public Shortcut(boolean isStatic, int rank) {
-            this(null);
-            mIsStatic = isStatic;
-            mRank = rank;
-            mId = generateId(isStatic, rank);
-        }
-
-        @Override
-        public boolean isDeclaredInManifest() {
-            return mIsStatic;
-        }
-
-        @Override
-        public boolean isDynamic() {
-            return !mIsStatic;
-        }
-
-        @Override
-        public int getRank() {
-            return mRank;
-        }
-
-        @Override
-        public String getId() {
-            return mId;
-        }
+    private ShortcutInfo createInfo(boolean isStatic, int rank) {
+        ShortcutInfo info = spy(new ShortcutInfo.Builder(
+                RuntimeEnvironment.application, generateId(isStatic, rank))
+                .setRank(rank)
+                .build());
+        doReturn(isStatic).when(info).isDeclaredInManifest();
+        doReturn(!isStatic).when(info).isDynamic();
+        return info;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 0e08276..d6f992f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -58,6 +58,7 @@
             TYPE_ON_BOARD_POPUP,
             TYPE_DISCOVERY_BOUNCE,
             TYPE_SNACKBAR,
+            TYPE_LISTENER,
 
             TYPE_TASK_MENU,
             TYPE_OPTIONS_POPUP
@@ -72,15 +73,16 @@
     public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
     public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6;
     public static final int TYPE_SNACKBAR = 1 << 7;
+    public static final int TYPE_LISTENER = 1 << 8;
 
     // Popups related to quickstep UI
-    public static final int TYPE_TASK_MENU = 1 << 8;
-    public static final int TYPE_OPTIONS_POPUP = 1 << 9;
+    public static final int TYPE_TASK_MENU = 1 << 9;
+    public static final int TYPE_OPTIONS_POPUP = 1 << 10;
 
     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
             | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
-            | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR;
+            | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER;
 
     // Type of popups which should be kept open during launcher rebind
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
@@ -90,7 +92,7 @@
     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
             | TYPE_SNACKBAR;
 
-    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE;
+    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER;
 
     // These view all have particular operation associated with swipe down interaction.
     public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
@@ -117,7 +119,7 @@
     }
 
     public final void close(boolean animate) {
-        animate &= !Utilities.isPowerSaverPreventingAnimation(getContext());
+        animate &= Utilities.areAnimationsEnabled(getContext());
         if (mIsOpen) {
             BaseActivity.fromContext(getContext()).getUserEventDispatcher()
                     .resetElapsedContainerMillis("container closed");
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index ed79914..d884049 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -81,8 +81,8 @@
         return super.dumpProperties() + " componentName=" + componentName;
     }
 
-    public ShortcutInfo makeShortcut() {
-        return new ShortcutInfo(this);
+    public WorkspaceItemInfo makeWorkspaceItem() {
+        return new WorkspaceItemInfo(this);
     }
 
     public ComponentKey toComponentKey() {
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 0d9bd31..9724869 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -391,7 +391,7 @@
                 return -1;
             }
 
-            mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINSTALL_ICON);
+            mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON);
             final Intent intent = new Intent(Intent.ACTION_MAIN, null)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setComponent(new ComponentName(packageName, className))
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 18599ac..c6fd906 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_SEARCH;
-
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
@@ -169,10 +167,10 @@
             intent.setSourceBounds(getViewBounds(v));
         }
         try {
-            boolean isShortcut = (item instanceof ShortcutInfo)
+            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                     || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
-                    && !((ShortcutInfo) item).isPromise();
+                    && !((WorkspaceItemInfo) item).isPromise();
             if (isShortcut) {
                 // Shortcuts need some special checks due to legacy reasons.
                 startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
@@ -209,7 +207,7 @@
                         .penaltyLog().build());
 
                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    String id = ((ShortcutInfo) info).getDeepShortcutId();
+                    String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
                     String packageName = intent.getPackage();
                     DeepShortcutManager.getInstance(this).startShortcut(
                             packageName, id, intent.getSourceBounds(), optsBundle, info.user);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4dd2e0a..3611ad4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -215,11 +215,11 @@
         mDotScaleAnim.start();
     }
 
-    public void applyFromShortcutInfo(ShortcutInfo info) {
-        applyFromShortcutInfo(info, false);
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
+        applyFromWorkspaceItem(info, false);
     }
 
-    public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
         if (promiseStateChanged || (info.hasPromiseIconUi())) {
@@ -232,7 +232,7 @@
     public void applyFromApplicationInfo(AppInfo info) {
         applyIconAndLabel(info);
 
-        // We don't need to check the info since it's not a ShortcutInfo
+        // We don't need to check the info since it's not a WorkspaceItemInfo
         super.setTag(info);
 
         // Verify high res immediately
@@ -247,7 +247,7 @@
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
         applyIconAndLabel(info);
-        // We don't need to check the info since it's not a ShortcutInfo
+        // We don't need to check the info since it's not a WorkspaceItemInfo
         super.setTag(info);
 
         // Verify high res immediately
@@ -480,8 +480,7 @@
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
         float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
-        float fromAlpha = toAlpha == 1 ? 0 : 1f;
-        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, fromAlpha, toAlpha);
+        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
     }
 
     @Override
@@ -492,11 +491,11 @@
     }
 
     public void applyPromiseState(boolean promiseStateChanged) {
-        if (getTag() instanceof ShortcutInfo) {
-            ShortcutInfo info = (ShortcutInfo) getTag();
+        if (getTag() instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
             final boolean isPromise = info.hasPromiseIconUi();
             final int progressLevel = isPromise ?
-                    ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
+                    ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
                             info.getInstallProgress() : 0)) : 100;
 
             PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
@@ -620,8 +619,8 @@
 
             if (info instanceof AppInfo) {
                 applyFromApplicationInfo((AppInfo) info);
-            } else if (info instanceof ShortcutInfo) {
-                applyFromShortcutInfo((ShortcutInfo) info);
+            } else if (info instanceof WorkspaceItemInfo) {
+                applyFromWorkspaceItem((WorkspaceItemInfo) info);
                 mActivity.invalidateParent(info);
             } else if (info instanceof PackageItemInfo) {
                 applyFromPackageItemInfo((PackageItemInfo) info);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a117cfd..9d9a639 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -1983,7 +1983,7 @@
             // Animations are disabled in power save mode, causing the repeated animation to jump
             // spastically between beginning and end states. Since this looks bad, we don't repeat
             // the animation in power save mode.
-            if (!Utilities.isPowerSaverPreventingAnimation(getContext())) {
+            if (Utilities.areAnimationsEnabled(getContext())) {
                 va.setRepeatMode(ValueAnimator.REVERSE);
                 va.setRepeatCount(ValueAnimator.INFINITE);
             }
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 5424a8f..b86e7c0 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -93,7 +93,8 @@
         if (mPendingCheckForLongPress != null) {
             if (com.android.launcher3.TestProtocol.sDebugTracing) {
                 android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
-                        "cancelLongPress");
+                        "cancelLongPress @ " + android.util.Log.getStackTraceString(
+                                new Throwable()));
             }
             mView.removeCallbacks(mPendingCheckForLongPress);
             mPendingCheckForLongPress = null;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 4c6824a..8cbad20 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -64,7 +62,7 @@
      */
     @Override
     public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
-        if (info instanceof ShortcutInfo) {
+        if (info instanceof WorkspaceItemInfo) {
             // Support the action unless the item is in a context menu.
             return info.screenId >= 0;
         }
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 21254ab..b747d62 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -50,7 +50,7 @@
     /**
      * The apps and shortcuts
      */
-    public ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
+    public ArrayList<WorkspaceItemInfo> contents = new ArrayList<WorkspaceItemInfo>();
 
     ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
 
@@ -64,14 +64,14 @@
      *
      * @param item
      */
-    public void add(ShortcutInfo item, boolean animate) {
+    public void add(WorkspaceItemInfo item, boolean animate) {
         add(item, contents.size(), animate);
     }
 
     /**
      * Add an app or shortcut for a specified rank.
      */
-    public void add(ShortcutInfo item, int rank, boolean animate) {
+    public void add(WorkspaceItemInfo item, int rank, boolean animate) {
         rank = Utilities.boundToRange(rank, 0, contents.size());
         contents.add(rank, item);
         for (int i = 0; i < listeners.size(); i++) {
@@ -85,7 +85,7 @@
      *
      * @param item
      */
-    public void remove(ShortcutInfo item, boolean animate) {
+    public void remove(WorkspaceItemInfo item, boolean animate) {
         contents.remove(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onRemove(item);
@@ -129,8 +129,8 @@
     }
 
     public interface FolderListener {
-        public void onAdd(ShortcutInfo item, int rank);
-        public void onRemove(ShortcutInfo item);
+        public void onAdd(WorkspaceItemInfo item, int rank);
+        public void onRemove(WorkspaceItemInfo item);
         public void onTitleChanged(CharSequence title);
         public void onItemsChanged(boolean animate);
         public void prepareAutoUpdate();
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 5c5f5e4..e9b932a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -25,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Handler;
@@ -44,7 +45,6 @@
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
@@ -244,16 +244,16 @@
         return convertToLauncherActivityIfPossible(info);
     }
 
-    public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
+    public static WorkspaceItemInfo fromShortcutIntent(Context context, Intent data) {
         PendingInstallShortcutInfo info = createPendingInfo(context, data);
-        return info == null ? null : (ShortcutInfo) info.getItemInfo().first;
+        return info == null ? null : (WorkspaceItemInfo) info.getItemInfo().first;
     }
 
-    public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
-        return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
+    public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+        return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
     }
 
-    public static void queueShortcut(ShortcutInfoCompat info, Context context) {
+    public static void queueShortcut(ShortcutInfo info, Context context) {
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
     }
 
@@ -327,7 +327,7 @@
     private static class PendingInstallShortcutInfo {
 
         final LauncherActivityInfo activityInfo;
-        final ShortcutInfoCompat shortcutInfo;
+        final ShortcutInfo shortcutInfo;
         final AppWidgetProviderInfo providerInfo;
 
         final Intent data;
@@ -372,7 +372,7 @@
         /**
          * Initializes a PendingInstallShortcutInfo to represent a launcher target.
          */
-        public PendingInstallShortcutInfo(ShortcutInfoCompat info, Context context) {
+        public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
             activityInfo = null;
             shortcutInfo = info;
             providerInfo = null;
@@ -381,7 +381,7 @@
             mContext = context;
             user = info.getUserHandle();
 
-            launchIntent = info.makeIntent();
+            launchIntent = ShortcutKey.makeIntent(info);
             label = info.getShortLabel().toString();
         }
 
@@ -483,11 +483,11 @@
                 // Set default values until proper values is loaded.
                 appInfo.title = "";
                 appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
-                final ShortcutInfo si = appInfo.makeShortcut();
+                final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
                 if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
                     app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
                 } else {
-                    app.getModel().updateAndBindShortcutInfo(() -> {
+                    app.getModel().updateAndBindWorkspaceItem(() -> {
                         app.getIconCache().getTitleAndIcon(
                                 si, activityInfo, false /* useLowResIcon */);
                         return si;
@@ -495,7 +495,7 @@
                 }
                 return Pair.create((ItemInfo) si, (Object) activityInfo);
             } else if (shortcutInfo != null) {
-                ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
+                WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
                 LauncherIcons li = LauncherIcons.obtain(mContext);
                 si.applyFrom(li.createShortcutIcon(shortcutInfo));
                 li.recycle();
@@ -513,7 +513,7 @@
                 widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
                 return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
             } else {
-                ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+                WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
                 return Pair.create((ItemInfo) si, null);
             }
         }
@@ -537,10 +537,10 @@
                 return info == null ? null : new PendingInstallShortcutInfo(info, context);
             } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
                 DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
-                List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+                List<ShortcutInfo> si = sm.queryForFullDetails(
                         decoder.launcherIntent.getPackage(),
                         Arrays.asList(decoder.launcherIntent.getStringExtra(
-                                ShortcutInfoCompat.EXTRA_SHORTCUT_ID)),
+                                ShortcutKey.EXTRA_SHORTCUT_ID)),
                         decoder.user);
                 if (si.isEmpty()) {
                     return null;
@@ -625,18 +625,18 @@
         return new PendingInstallShortcutInfo(info, original.mContext);
     }
 
-    private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
+    private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
         Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
 
         if (intent == null) {
-            // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
-            Log.e(TAG, "Can't construct ShorcutInfo with null intent");
+            // If the intent is null, return null as we can't construct a valid WorkspaceItemInfo
+            Log.e(TAG, "Can't construct WorkspaceItemInfo with null intent");
             return null;
         }
 
-        final ShortcutInfo info = new ShortcutInfo();
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
 
         // Only support intents for current user for now. Intents sent from other
         // users wouldn't get here without intent forwarding anyway.
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 93c20ba..3e7f67b 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,8 +16,9 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
 import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetHostView;
@@ -25,7 +26,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -578,9 +578,7 @@
         private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
 
         OverlayMonitor(Context context) {
-            IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
-            filter.addDataScheme("package");
-            context.registerReceiver(this, filter);
+            context.registerReceiver(this, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
         }
 
         @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7051a67..d790c04 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -59,7 +59,6 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -1019,7 +1018,7 @@
      *
      * @param info The data structure describing the shortcut.
      */
-    View createShortcut(ShortcutInfo info) {
+    View createShortcut(WorkspaceItemInfo info) {
         return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
     }
 
@@ -1031,10 +1030,10 @@
      *
      * @return A View inflated from layoutResId.
      */
-    public View createShortcut(ViewGroup parent, ShortcutInfo info) {
+    public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
         BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.app_icon, parent, false);
-        favorite.applyFromShortcutInfo(info);
+        favorite.applyFromWorkspaceItem(info);
         favorite.setOnClickListener(ItemClickHandler.INSTANCE);
         favorite.setOnFocusChangeListener(mFocusHandler);
         return favorite;
@@ -1055,9 +1054,9 @@
         int[] cellXY = mTmpAddItemCellCoordinates;
         CellLayout layout = getCellLayout(container, screenId);
 
-        ShortcutInfo info = null;
+        WorkspaceItemInfo info = null;
         if (Utilities.ATLEAST_OREO) {
-            info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+            info = LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
                     this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
         }
 
@@ -1579,11 +1578,11 @@
      * @param deleteFromDb whether or not to delete this item from the db.
      */
     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
-        if (itemInfo instanceof ShortcutInfo) {
+        if (itemInfo instanceof WorkspaceItemInfo) {
             // Remove the shortcut from the folder before removing it from launcher
             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
             if (folderIcon instanceof FolderIcon) {
-                ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo, true);
+                ((FolderInfo) folderIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
             } else {
                 mWorkspace.removeWorkspaceItem(v);
             }
@@ -1908,7 +1907,7 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
-                    ShortcutInfo info = (ShortcutInfo) item;
+                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
                     view = createShortcut(info);
                     break;
                 }
@@ -2224,10 +2223,6 @@
      */
     public void bindAllApplications(ArrayList<AppInfo> apps) {
         mAppsView.getAppsStore().setApps(apps);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.bindAllApplications(apps);
-        }
     }
 
     /**
@@ -2266,7 +2261,7 @@
      * @param updated list of shortcuts which have changed.
      */
     @Override
-    public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, final UserHandle user) {
+    public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) {
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
         }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 34bdb3c..edac516 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -57,16 +57,10 @@
      * Extension points for providing custom behavior on certain user interactions.
      */
     void onLauncherProviderChange();
-    void bindAllApplications(ArrayList<AppInfo> apps);
 
     /**
      * Starts a search with {@param initialQuery}. Return false if search was not started.
      */
     boolean startSearch(
             String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
-
-    /*
-     * Extensions points for adding / replacing some other aspects of the Launcher experience.
-     */
-    boolean hasSettings();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c559f2b..e788ceb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -22,6 +22,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -48,7 +49,6 @@
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -149,7 +149,7 @@
         public void bindAppsAdded(IntArray newScreens,
                 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
         public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
-        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, UserHandle user);
+        public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
@@ -213,6 +213,7 @@
         synchronized (mLock) {
             Preconditions.assertUIThread();
             mCallbacks = new WeakReference<>(callbacks);
+            android.util.Log.d("b/131170582", "mCallbacks = " + mCallbacks);
         }
     }
 
@@ -267,12 +268,12 @@
     }
 
     @Override
-    public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
     }
 
-    public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
     }
@@ -330,6 +331,7 @@
             // Stop any existing loaders first, so they don't set mModelLoaded to true later
             stopLoader();
             mModelLoaded = false;
+            android.util.Log.d("b/131170582", "1 mModelLoaded = " + mModelLoaded);
         }
 
         // Start the loader if launcher is already running, otherwise the loader will run,
@@ -390,6 +392,7 @@
         synchronized (mLock) {
             LoaderTask oldTask = mLoaderTask;
             mLoaderTask = null;
+            android.util.Log.d("b/131170582", "1 mLoaderTask = " + mLoaderTask);
             if (oldTask != null) {
                 oldTask.stopLocked();
             }
@@ -400,6 +403,7 @@
         synchronized (mLock) {
             stopLoader();
             mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+            android.util.Log.d("b/131170582", "2 mLoaderTask = " + mLoaderTask);
             runOnWorkerThread(mLoaderTask);
         }
     }
@@ -444,6 +448,7 @@
                 mTask = task;
                 mIsLoaderTaskRunning = true;
                 mModelLoaded = false;
+                android.util.Log.d("b/131170582", "2 mModelLoaded = " + mModelLoaded);
             }
         }
 
@@ -451,6 +456,7 @@
             synchronized (mLock) {
                 // Everything loaded bind the data.
                 mModelLoaded = true;
+                android.util.Log.d("b/131170582", "3 mModelLoaded = " + mModelLoaded);
             }
         }
 
@@ -460,6 +466,7 @@
                 // If we are still the last one to be scheduled, remove ourselves.
                 if (mLoaderTask == mTask) {
                     mLoaderTask = null;
+                    android.util.Log.d("b/131170582", "3 mLoaderTask = " + mLoaderTask);
                 }
                 mIsLoaderTaskRunning = false;
             }
@@ -530,8 +537,8 @@
 
     }
 
-    public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
-        updateAndBindShortcutInfo(() -> {
+    public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
+        updateAndBindWorkspaceItem(() -> {
             si.updateFromDeepShortcutInfo(info, mApp.getContext());
             LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
             si.applyFrom(li.createShortcutIcon(info));
@@ -543,15 +550,15 @@
     /**
      * Utility method to update a shortcut on the background thread.
      */
-    public void updateAndBindShortcutInfo(final Supplier<ShortcutInfo> shortcutProvider) {
+    public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) {
         enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                ShortcutInfo info = shortcutProvider.get();
+                WorkspaceItemInfo info = itemProvider.get();
                 getModelWriter().updateItemInDatabase(info);
-                ArrayList<ShortcutInfo> update = new ArrayList<>();
+                ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
                 update.add(info);
-                bindUpdatedShortcuts(update, info.user);
+                bindUpdatedWorkspaceItems(update);
             }
         });
     }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e738eb7..e6c2d0c 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -12,12 +12,16 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.WindowInsets;
 
+import java.util.Collections;
+import java.util.List;
+
 public class LauncherRootView extends InsettableFrameLayout {
 
     private final Launcher mLauncher;
@@ -30,8 +34,14 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private final RectF mTouchExcludeRegion = new RectF();
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+            Collections.singletonList(new Rect());
+
     private View mAlignedView;
     private WindowStateListener mWindowStateListener;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mDisallowBackGesture;
 
     public LauncherRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -177,10 +187,38 @@
         return super.dispatchTouchEvent(ev);
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
+        setDisallowBackGesture(mDisallowBackGesture);
+    }
+
+    @TargetApi(Build.VERSION_CODES.Q)
+    public void setDisallowBackGesture(boolean disallowBackGesture) {
+        if (!Utilities.ATLEAST_Q) {
+            return;
+        }
+        mDisallowBackGesture = disallowBackGesture;
+        setSystemGestureExclusionRects(mDisallowBackGesture
+                ? SYSTEM_GESTURE_EXCLUSION_RECT
+                : Collections.emptyList());
+    }
+
     public interface WindowStateListener {
 
         void onWindowFocusChanged(boolean hasFocus);
 
         void onWindowVisibilityChanged(int visibility);
     }
+
+    @Override
+    public void requestLayout() {
+        super.requestLayout();
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "requestLayout @ " + android.util.Log.getStackTraceString(
+                            new Throwable()));
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 124574c..51079b0 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -18,6 +18,7 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -30,7 +31,6 @@
 
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.uioverrides.states.AllAppsState;
@@ -58,6 +58,7 @@
     public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
     public static final int ALL_APPS_CONTENT = 1 << 4;
     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
+    public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
 
     protected static final int FLAG_MULTI_PAGE = 1 << 0;
     protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 5b654d8..209578d 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -140,6 +140,10 @@
         return mState;
     }
 
+    public LauncherState getCurrentStableState() {
+        return mCurrentStableState;
+    }
+
     public StateHandler[] getStateHandlers() {
         if (mStateHandlers == null) {
             mStateHandlers = UiFactory.getStateHandler(mLauncher);
@@ -218,6 +222,7 @@
 
     private void goToState(LauncherState state, boolean animated, long delay,
             final Runnable onCompleteRunnable) {
+        animated &= Utilities.areAnimationsEnabled(mLauncher);
         if (mLauncher.isInState(state)) {
             if (mConfig.mCurrentAnimation == null) {
                 // Run any queued runnable
@@ -320,8 +325,12 @@
             if (!isWorkspaceVisible) {
                 workspace.setScaleX(0.92f);
                 workspace.setScaleY(0.92f);
-                workspace.getHotseat().setScaleX(0.92f);
-                workspace.getHotseat().setScaleY(0.92f);
+            }
+            Hotseat hotseat = workspace.getHotseat();
+            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
+            if (!isHotseatVisible) {
+                hotseat.setScaleX(0.92f);
+                hotseat.setScaleY(0.92f);
             }
         } else if (fromState == NORMAL && toState == OVERVIEW_PEEK) {
             builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ed77786..abb45e5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -98,6 +98,7 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
+    protected int mMinScrollX;
     protected int mMaxScrollX;
     protected OverScroller mScroller;
     private Interpolator mDefaultInterpolator;
@@ -266,11 +267,40 @@
     }
 
     private int validateNewPage(int newPage) {
+        newPage = ensureWithinScrollBounds(newPage);
         // Ensure that it is clamped by the actual set of children in all cases
         return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
     }
 
     /**
+     * @return The closest page to the provided page that is within mMinScrollX and mMaxScrollX.
+     */
+    private int ensureWithinScrollBounds(int page) {
+        int dir = !mIsRtl ? 1 : - 1;
+        int currScroll = getScrollForPage(page);
+        int prevScroll;
+        while (currScroll < mMinScrollX) {
+            page += dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll <= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page > mMinScrollX");
+                break;
+            }
+        }
+        while (currScroll > mMaxScrollX) {
+            page -= dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll >= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page < mMaxScrollX");
+                break;
+            }
+        }
+        return page;
+    }
+
+    /**
      * Sets the current page.
      */
     public void setCurrentPage(int currentPage) {
@@ -348,29 +378,29 @@
     public void scrollTo(int x, int y) {
         mUnboundedScrollX = x;
 
-        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
-        boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
+        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX);
+        boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX);
 
         if (!isXBeforeFirstPage && !isXAfterLastPage) {
             mSpringOverScrollX = 0;
         }
 
         if (isXBeforeFirstPage) {
-            super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
+            super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
                     overScroll(x - mMaxScrollX);
                 } else {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 }
             }
         } else if (isXAfterLastPage) {
-            super.scrollTo(mIsRtl ? 0 : mMaxScrollX, y);
+            super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 } else {
                     overScroll(x - mMaxScrollX);
                 }
@@ -557,12 +587,12 @@
                     // Wait until all transitions are complete.
                     if (!transition.isRunning()) {
                         transition.removeTransitionListener(this);
-                        updateMaxScrollX();
+                        updateMinAndMaxScrollX();
                     }
                 }
             });
         } else {
-            updateMaxScrollX();
+            updateMinAndMaxScrollX();
         }
 
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
@@ -627,12 +657,13 @@
         return 0;
     }
 
-    private void updateMaxScrollX() {
+    protected void updateMinAndMaxScrollX() {
+        mMinScrollX = computeMinScrollX();
         mMaxScrollX = computeMaxScrollX();
     }
 
-    public int getMaxScrollX() {
-        return mMaxScrollX;
+    protected int computeMinScrollX() {
+        return 0;
     }
 
     protected int computeMaxScrollX() {
@@ -1010,12 +1041,8 @@
             return;
         }
 
-        if (overScrollAmount < 0) {
-            super.scrollTo(overScrollAmount, getScrollY());
-        } else {
-            int x = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
-            super.scrollTo(x + overScrollAmount, getScrollY());
-        }
+        int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX);
+        super.scrollTo(x + overScrollAmount, getScrollY());
         invalidate();
     }
 
@@ -1030,7 +1057,7 @@
 
         if (mFreeScroll && !mScroller.isFinished()) {
             if (amount < 0) {
-                super.scrollTo(amount, getScrollY());
+                super.scrollTo(mMinScrollX + amount, getScrollY());
             } else {
                 super.scrollTo(mMaxScrollX + amount, getScrollY());
             }
@@ -1169,12 +1196,12 @@
                     int initialScrollX = getScrollX();
 
                     if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) ||
-                            ((initialScrollX <= 0) && (!isVelocityXLeft || !isFling))) {
-                        mScroller.springBack(getScrollX(), 0, mMaxScrollX);
+                            ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) {
+                        mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX);
                     } else {
                         mScroller.setInterpolator(mDefaultInterpolator);
                         mScroller.fling(initialScrollX, -velocityX,
-                                0, mMaxScrollX,
+                                mMinScrollX, mMaxScrollX,
                                 Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
 
                         int finalX = mScroller.getFinalPos();
@@ -1182,12 +1209,13 @@
 
                         int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
                         int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                        if (finalX > 0 && finalX < mMaxScrollX) {
+                        if (finalX > mMinScrollX && finalX < mMaxScrollX) {
                             // If scrolling ends in the half of the added space that is closer to
                             // the end, settle to the end. Otherwise snap to the nearest page.
                             // If flinging past one of the ends, don't change the velocity as it
                             // will get stopped at the end anyway.
-                            int pageSnappedX = finalX < firstPageScroll / 2 ? 0
+                            int pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2
+                                    ? mMinScrollX
                                     : finalX > (lastPageScroll + mMaxScrollX) / 2
                                             ? mMaxScrollX
                                             : getScrollForPage(mNextPage);
@@ -1347,7 +1375,7 @@
     }
 
     protected boolean isInOverScroll() {
-        return (getScrollX() > mMaxScrollX || getScrollX() < 0);
+        return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX);
     }
 
     protected int getPageSnapDuration() {
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
index ea151cd..4ad0b3d 100644
--- a/src/com/android/launcher3/PromiseAppInfo.java
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -38,13 +38,13 @@
     }
 
     @Override
-    public ShortcutInfo makeShortcut() {
-        ShortcutInfo shortcut = new ShortcutInfo(this);
+    public WorkspaceItemInfo makeWorkspaceItem() {
+        WorkspaceItemInfo shortcut = new WorkspaceItemInfo(this);
         shortcut.setInstallProgress(level);
         // We need to update the component name when the apk is installed
-        shortcut.status |= ShortcutInfo.FLAG_AUTOINSTALL_ICON;
+        shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
         // Since the user is manually placing it on homescreen, it should not be auto-removed later
-        shortcut.status |= ShortcutInfo.FLAG_RESTORE_STARTED;
+        shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
         return shortcut;
     }
 
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 7d3715e..dab4282 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -66,4 +66,6 @@
 
     public static boolean sDebugTracing = false;
     public static final String NO_DRAG_TAG = "b/129434166";
+    public static final String REQUEST_ENABLE_DRAG_LOGGING = "enable-drag-logging";
+    public static final String REQUEST_DISABLE_DRAG_LOGGING = "disable-drag-logging";
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index fd4b508..382eedd 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 
+import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
@@ -32,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -66,7 +68,6 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -517,12 +518,10 @@
                 LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
-    public static boolean isPowerSaverPreventingAnimation(Context context) {
-        if (ATLEAST_P) {
-            // Battery saver mode no longer prevents animations.
-            return false;
-        }
-        return context.getSystemService(PowerManager.class).isPowerSaveMode();
+    public static boolean areAnimationsEnabled(Context context) {
+        return ATLEAST_OREO
+                ? ValueAnimator.areAnimatorsEnabled()
+                : !context.getSystemService(PowerManager.class).isPowerSaveMode();
     }
 
     public static boolean isWallpaperAllowed(Context context) {
@@ -626,7 +625,7 @@
     /**
      * Returns the full drawable for {@param info}.
      * @param outObj this is set to the internal data associated with {@param info},
-     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
      */
     public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
             boolean flattenDrawable, Object[] outObj) {
@@ -646,7 +645,7 @@
             }
             ShortcutKey key = ShortcutKey.fromItemInfo(info);
             DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
-            List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+            List<ShortcutInfo> si = sm.queryForFullDetails(
                     key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
             if (si.isEmpty()) {
                 return null;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2ee537c..8849768 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -956,7 +956,8 @@
 
     private boolean isScrollingOverlay() {
         return mLauncherOverlay != null &&
-                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
+                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX)
+                        || (!mIsRtl && getUnboundedScrollX() < mMinScrollX));
     }
 
     @Override
@@ -1609,7 +1610,7 @@
             return false;
         }
 
-        boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
+        boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo);
         boolean willBecomeShortcut =
                 (info.itemType == ITEM_TYPE_APPLICATION ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
@@ -1658,12 +1659,12 @@
         mCreateUserFolderOnDrop = false;
         final int screenId = getIdForScreen(target);
 
-        boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
-        boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
+        boolean aboveShortcut = (v.getTag() instanceof WorkspaceItemInfo);
+        boolean willBecomeShortcut = (newView.getTag() instanceof WorkspaceItemInfo);
 
         if (aboveShortcut && willBecomeShortcut) {
-            ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
-            ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
+            WorkspaceItemInfo sourceInfo = (WorkspaceItemInfo) newView.getTag();
+            WorkspaceItemInfo destInfo = (WorkspaceItemInfo) v.getTag();
             // if the drag started here, we need to remove it from the workspace
             if (!external) {
                 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
@@ -2418,8 +2419,8 @@
      */
     private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
         if (d.dragInfo instanceof PendingAddShortcutInfo) {
-            ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
-                    .activityInfo.createShortcutInfo();
+            WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
+                    .activityInfo.createWorkspaceItemInfo();
             if (si != null) {
                 d.dragInfo = si;
             }
@@ -2524,10 +2525,10 @@
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                 if (info.container == NO_ID && info instanceof AppInfo) {
                     // Came from all apps -- make a copy
-                    info = ((AppInfo) info).makeShortcut();
+                    info = ((AppInfo) info).makeWorkspaceItem();
                     d.dragInfo = info;
                 }
-                view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
+                view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
@@ -2916,7 +2917,7 @@
         final Workspace.ItemOperator packageAndUserInFolder = (info, view) -> {
             if (info instanceof FolderInfo) {
                 FolderInfo folderInfo = (FolderInfo) info;
-                for (ShortcutInfo shortcutInfo : folderInfo.contents) {
+                for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
                     if (packageAndUser.evaluate(shortcutInfo, view)) {
                         return true;
                     }
@@ -3045,7 +3046,7 @@
                     if (parent != null) {
                         FolderInfo folderInfo = (FolderInfo) parent.getTag();
                         folderInfo.prepareAutoUpdate();
-                        folderInfo.remove((ShortcutInfo) itemToRemove, false);
+                        folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
                     }
                 }
             }
@@ -3112,13 +3113,13 @@
         return false;
     }
 
-    void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
+    void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
         int total  = shortcuts.size();
-        final HashSet<ShortcutInfo> updates = new HashSet<>(total);
+        final HashSet<WorkspaceItemInfo> updates = new HashSet<>(total);
         final IntSet folderIds = new IntSet();
 
         for (int i = 0; i < total; i++) {
-            ShortcutInfo s = shortcuts.get(i);
+            WorkspaceItemInfo s = shortcuts.get(i);
             updates.add(s);
             folderIds.add(s.container);
         }
@@ -3126,14 +3127,14 @@
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView &&
                         updates.contains(info)) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     BubbleTextView shortcut = (BubbleTextView) v;
                     Drawable oldIcon = shortcut.getIcon();
                     boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
-                    shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
+                    shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
                 }
                 // process all the shortcuts
                 return false;
@@ -3159,7 +3160,7 @@
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
                     if (!packageUserKey.updateFromItemInfo(info)
                             || updatedDots.test(packageUserKey)) {
                         ((BubbleTextView) v).applyDotState(info, true /* animate */);
@@ -3178,7 +3179,7 @@
                 if (info instanceof FolderInfo && folderIds.contains(info.id)
                         && v instanceof FolderIcon) {
                     FolderDotInfo folderDotInfo = new FolderDotInfo();
-                    for (ShortcutInfo si : ((FolderInfo) info).contents) {
+                    for (WorkspaceItemInfo si : ((FolderInfo) info).contents) {
                         folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
                     }
                     ((FolderIcon) v).setDotInfo(folderDotInfo);
@@ -3201,7 +3202,7 @@
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
                         && updates.contains(info)) {
                     ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
                 } else if (v instanceof PendingAppWidgetHostView
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
similarity index 87%
rename from src/com/android/launcher3/ShortcutInfo.java
rename to src/com/android/launcher3/WorkspaceItemInfo.java
index e53d59c..5a2373b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -19,17 +19,18 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.text.TextUtils;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ContentWriter;
 
 /**
  * Represents a launchable icon on the workspaces and in folders.
  */
-public class ShortcutInfo extends ItemInfoWithIcon {
+public class WorkspaceItemInfo extends ItemInfoWithIcon {
 
     public static final int DEFAULT = 0;
 
@@ -86,11 +87,11 @@
      */
     private int mInstallProgress;
 
-    public ShortcutInfo() {
+    public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
 
-    public ShortcutInfo(ShortcutInfo info) {
+    public WorkspaceItemInfo(WorkspaceItemInfo info) {
         super(info);
         title = info.title;
         intent = new Intent(info.intent);
@@ -99,17 +100,17 @@
         mInstallProgress = info.mInstallProgress;
     }
 
-    /** TODO: Remove this.  It's only called by ApplicationInfo.makeShortcut. */
-    public ShortcutInfo(AppInfo info) {
+    /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
+    public WorkspaceItemInfo(AppInfo info) {
         super(info);
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
     }
 
     /**
-     * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}.
+     * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
      */
-    public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+    public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
         user = shortcutInfo.getUserHandle();
         itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
         updateFromDeepShortcutInfo(shortcutInfo, context);
@@ -158,9 +159,9 @@
         status |= FLAG_INSTALL_SESSION_ACTIVE;
     }
 
-    public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
-        // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent
-        intent = shortcutInfo.makeIntent();
+    public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
+        // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
+        intent = ShortcutKey.makeIntent(shortcutInfo);
         title = shortcutInfo.getShortLabel();
 
         CharSequence label = shortcutInfo.getLongLabel();
@@ -176,10 +177,10 @@
         disabledMessage = shortcutInfo.getDisabledMessage();
     }
 
-    /** Returns the ShortcutInfo id associated with the deep shortcut. */
+    /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
     public String getDeepShortcutId() {
         return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
-                getIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
+                getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 45bade8..fd4df52 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -145,7 +145,7 @@
     }
 
     private boolean itemSupportsAccessibleDrag(ItemInfo item) {
-        if (item instanceof ShortcutInfo) {
+        if (item instanceof WorkspaceItemInfo) {
             // Support the action unless the item is in a context menu.
             return item.screenId >= 0;
         }
@@ -173,7 +173,7 @@
                 @Override
                 public void run() {
                     if (item instanceof AppInfo) {
-                        ShortcutInfo info = ((AppInfo) item).makeShortcut();
+                        WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
                         mLauncher.getModelWriter().addItemToDatabase(info,
                                 Favorites.CONTAINER_DESKTOP,
                                 screenId, coordinates[0], coordinates[1]);
@@ -195,7 +195,7 @@
         } else if (action == MOVE_TO_WORKSPACE) {
             Folder folder = Folder.getOpen(mLauncher);
             folder.close(true);
-            ShortcutInfo info = (ShortcutInfo) item;
+            WorkspaceItemInfo info = (WorkspaceItemInfo) item;
             folder.getInfo().remove(info, false);
 
             final int[] coordinates = new int[2];
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index f37f70b..c0c0b37 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -27,7 +27,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationMainView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
@@ -64,7 +64,7 @@
             if (!(host.getParent() instanceof DeepShortcutView)) {
                 return false;
             }
-            final ShortcutInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
+            final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
             Runnable onComplete = new Runnable() {
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 1c088db..17daeb8 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -27,7 +27,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.dragndrop.DragLayer;
 
@@ -106,7 +106,7 @@
                 // For icons, we can consider cells that have another icon or a folder.
                 ItemInfo info = (ItemInfo) child.getTag();
                 if (info instanceof AppInfo || info instanceof FolderInfo ||
-                        info instanceof ShortcutInfo) {
+                        info instanceof WorkspaceItemInfo) {
                     return id;
                 }
             }
@@ -125,7 +125,7 @@
             return mContext.getString(R.string.item_moved);
         } else {
             ItemInfo info = (ItemInfo) child.getTag();
-            if (info instanceof AppInfo || info instanceof ShortcutInfo) {
+            if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
                 return mContext.getString(R.string.folder_created);
 
             } else if (info instanceof FolderInfo) {
@@ -170,14 +170,14 @@
 
     public static String getDescriptionForDropOver(View overChild, Context context) {
         ItemInfo info = (ItemInfo) overChild.getTag();
-        if (info instanceof ShortcutInfo) {
+        if (info instanceof WorkspaceItemInfo) {
             return context.getString(R.string.create_folder_with, info.title);
         } else if (info instanceof FolderInfo) {
             if (TextUtils.isEmpty(info.title)) {
                 // Find the first item in the folder.
                 FolderInfo folder = (FolderInfo) info;
-                ShortcutInfo firstItem = null;
-                for (ShortcutInfo shortcut : folder.contents) {
+                WorkspaceItemInfo firstItem = null;
+                for (WorkspaceItemInfo shortcut : folder.contents) {
                     if (firstItem == null || firstItem.rank > shortcut.rank) {
                         firstItem = shortcut;
                     }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index e7d4679..4275f31 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -21,12 +21,12 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.List;
@@ -43,7 +43,7 @@
         void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
         void onPackagesSuspended(String[] packageNames, UserHandle user);
         void onPackagesUnsuspended(String[] packageNames, UserHandle user);
-        void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+        void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
                 UserHandle user);
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index b641391..fc48ba7 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -32,7 +32,6 @@
 import android.util.ArrayMap;
 
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
@@ -179,12 +178,7 @@
         public void onShortcutsChanged(@NonNull String packageName,
             @NonNull List<ShortcutInfo> shortcuts,
             @NonNull UserHandle user) {
-            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
-            for (ShortcutInfo shortcutInfo : shortcuts) {
-                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
-            }
-
-            mCallback.onShortcutsChanged(packageName, shortcutInfoCompats, user);
+            mCallback.onShortcutsChanged(packageName, shortcuts, user);
         }
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 82617fe..6e7a1bd 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -24,6 +24,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.os.Build;
 import android.os.Parcelable;
 import android.os.Process;
@@ -31,10 +32,9 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -103,12 +103,12 @@
      * thread to UI thread.
      * If (d) happens before we add this shortcut to our model, we will end up unpinning
      * the shortcut in the system.
-     * Here its the caller's responsibility to add the newly created ShortcutInfo immediately
+     * Here its the caller's responsibility to add the newly created WorkspaceItemInfo immediately
      * to the model (which may involves a single post-to-worker-thread). That will guarantee
      * that (d) happens after model is updated.
      */
     @Nullable
-    public static ShortcutInfo createShortcutInfoFromPinItemRequest(
+    public static WorkspaceItemInfo createWorkspaceItemFromPinItemRequest(
             Context context, final PinItemRequest request, final long acceptDelay) {
         if (request != null &&
                 request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
@@ -135,14 +135,14 @@
                 });
             }
 
-            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
-            ShortcutInfo info = new ShortcutInfo(compat, context);
+            ShortcutInfo si = request.getShortcutInfo();
+            WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
             // Apply the unbadged icon and fetch the actual icon asynchronously.
             LauncherIcons li = LauncherIcons.obtain(context);
-            info.applyFrom(li.createShortcutIcon(compat, false /* badged */));
+            info.applyFrom(li.createShortcutIcon(si, false /* badged */));
             li.recycle();
             LauncherAppState.getInstance(context).getModel()
-                    .updateAndBindShortcutInfo(info, compat);
+                    .updateAndBindWorkspaceItem(info, si);
             return info;
         } else {
             return null;
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index 76eec6d..ace5691 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -32,11 +32,11 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
 
 /**
  * Wrapper class for representing a shortcut configure activity.
@@ -70,10 +70,10 @@
     public abstract Drawable getFullResIcon(IconCache cache);
 
     /**
-     * Return a shortcut info, if it can be created directly on drop, without requiring any
+     * Return a WorkspaceItemInfo, if it can be created directly on drop, without requiring any
      * {@link #startConfigActivity(Activity, int)}.
      */
-    public ShortcutInfo createShortcutInfo() {
+    public WorkspaceItemInfo createWorkspaceItemInfo() {
         return null;
     }
 
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 5ac9867..a72089d 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -52,7 +52,6 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompatVO;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.InstantAppResolver;
@@ -250,8 +249,7 @@
      */
     public void onPlaceAutomaticallyClick(View v) {
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
-            InstallShortcutReceiver.queueShortcut(
-                    new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
+            InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
             logCommand(Action.Command.CONFIRM);
             mRequest.accept();
             finish();
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 8b100d9..4d45ba9 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -39,7 +39,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Thunk;
@@ -288,7 +288,7 @@
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             ItemInfo dragInfo = mDragObject.dragInfo;
-            if (dragInfo instanceof ShortcutInfo) {
+            if (dragInfo instanceof WorkspaceItemInfo) {
                 ComponentName cn = dragInfo.getTargetComponent();
                 if (cn != null && matcher.matches(dragInfo, cn)) {
                     cancelDrag();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6cc49de..9f902ed 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -24,12 +24,10 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -45,7 +43,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.folder.Folder;
@@ -57,8 +54,6 @@
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 
 /**
  * A ViewGroup that coordinates dragging across its descendants
@@ -73,9 +68,6 @@
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
 
-    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
-            Collections.singletonList(new Rect());
-
     @Thunk DragController mDragController;
 
     // Variables relating to animation of views after drop
@@ -94,8 +86,6 @@
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
     private final WorkspaceAndHotseatScrim mScrim;
 
-    private boolean mDisallowBackGesture;
-
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -562,24 +552,6 @@
         mScrim.onInsetsChanged(insets);
     }
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
-        setDisallowBackGesture(mDisallowBackGesture);
-    }
-
-    @TargetApi(Build.VERSION_CODES.Q)
-    public void setDisallowBackGesture(boolean disallowBackGesture) {
-        if (!Utilities.ATLEAST_Q) {
-            return;
-        }
-        mDisallowBackGesture = disallowBackGesture;
-        setSystemGestureExclusionRects(mDisallowBackGesture
-                ? SYSTEM_GESTURE_EXCLUSION_RECT
-                : Collections.emptyList());
-    }
-
     public WorkspaceAndHotseatScrim getScrim() {
         return mScrim;
     }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index bdbea29..3ab97b0 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,6 +24,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -54,7 +55,6 @@
 import com.android.launcher3.FirstFrameAnimatorHelper;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
@@ -321,11 +321,11 @@
             boolean iconBadged = (info instanceof ItemInfoWithIcon)
                     && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
             if ((info.id == ItemInfo.NO_ID && !iconBadged)
-                    || !(obj instanceof ShortcutInfoCompat)) {
+                    || !(obj instanceof ShortcutInfo)) {
                 // The item is not yet added on home screen.
                 return new FixedSizeEmptyDrawable(iconSize);
             }
-            ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
+            ShortcutInfo si = (ShortcutInfo) obj;
             LauncherIcons li = LauncherIcons.obtain(appState.getContext());
             Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
             li.recycle();
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 64655cc..91a31aa 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -29,6 +29,7 @@
 import android.os.Process;
 
 import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherAppState;
@@ -81,13 +82,13 @@
     }
 
     @Override
-    public com.android.launcher3.ShortcutInfo createShortcutInfo() {
+    public WorkspaceItemInfo createWorkspaceItemInfo() {
         // Total duration for the drop animation to complete.
         long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) +
                 LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
                 LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
         // Delay the actual accept() call until the drop animation is complete.
-        return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+        return LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
                 mContext, mRequest, duration);
     }
 
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2ce6634..4dbff1c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -61,7 +61,7 @@
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.config.FeatureFlags;
@@ -236,8 +236,8 @@
 
     public boolean startDrag(View v, DragOptions options) {
         Object tag = v.getTag();
-        if (tag instanceof ShortcutInfo) {
-            ShortcutInfo item = (ShortcutInfo) tag;
+        if (tag instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
 
             mEmptyCellRank = item.rank;
             mCurrentDragView = v;
@@ -269,13 +269,13 @@
         }
 
         mContent.removeItem(mCurrentDragView);
-        if (dragObject.dragInfo instanceof ShortcutInfo) {
+        if (dragObject.dragInfo instanceof WorkspaceItemInfo) {
             mItemsInvalidated = true;
 
             // We do not want to get events for the item being removed, as they will get handled
             // when the drop completes
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-                mInfo.remove((ShortcutInfo) dragObject.dragInfo, true);
+                mInfo.remove((WorkspaceItemInfo) dragObject.dragInfo, true);
             }
         }
         mDragInProgress = true;
@@ -379,7 +379,7 @@
 
     void bind(FolderInfo info) {
         mInfo = info;
-        ArrayList<ShortcutInfo> children = info.contents;
+        ArrayList<WorkspaceItemInfo> children = info.contents;
         Collections.sort(children, ITEM_POS_COMPARATOR);
         mContent.bindItems(children);
 
@@ -800,7 +800,7 @@
             }
         } else {
             // The drag failed, we need to return the item to the folder
-            ShortcutInfo info = (ShortcutInfo) d.dragInfo;
+            WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
             View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
                     ? mCurrentDragView : mContent.createNewView(info);
             ArrayList<View> views = getItemsInReadingOrder();
@@ -1023,7 +1023,7 @@
                         // folder
                         CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
                                 mInfo.screenId);
-                        ShortcutInfo finalItem = mInfo.contents.remove(0);
+                        WorkspaceItemInfo finalItem = mInfo.contents.remove(0);
                         newIcon = mLauncher.createShortcut(cellLayout, finalItem);
                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
                                 mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
@@ -1111,9 +1111,9 @@
 
         PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
                 ? (PendingAddShortcutInfo) d.dragInfo : null;
-        ShortcutInfo pasiSi = pasi != null ? pasi.activityInfo.createShortcutInfo() : null;
+        WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
         if (pasi != null && pasiSi == null) {
-            // There is no ShortcutInfo, so we have to go through a configuration activity.
+            // There is no WorkspaceItemInfo, so we have to go through a configuration activity.
             pasi.container = mInfo.id;
             pasi.rank = mEmptyCellRank;
 
@@ -1122,15 +1122,15 @@
             d.deferDragViewCleanupPostAnimation = false;
             mRearrangeOnClose = true;
         } else {
-            final ShortcutInfo si;
+            final WorkspaceItemInfo si;
             if (pasiSi != null) {
                 si = pasiSi;
             } else if (d.dragInfo instanceof AppInfo) {
                 // Came from all apps -- make a copy.
-                si = ((AppInfo) d.dragInfo).makeShortcut();
+                si = ((AppInfo) d.dragInfo).makeWorkspaceItem();
             } else {
-                // ShortcutInfo
-                si = (ShortcutInfo) d.dragInfo;
+                // WorkspaceItemInfo
+                si = (WorkspaceItemInfo) d.dragInfo;
             }
 
             View currentDragView;
@@ -1138,7 +1138,7 @@
                 currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
 
                 // Actually move the item in the database if it was an external drag. Call this
-                // before creating the view, so that ShortcutInfo is updated appropriately.
+                // before creating the view, so that WorkspaceItemInfo is updated appropriately.
                 mLauncher.getModelWriter().addOrMoveItemInDatabase(
                         si, mInfo.id, 0, si.cellX, si.cellY);
 
@@ -1194,17 +1194,17 @@
     // This is used so the item doesn't immediately appear in the folder when added. In one case
     // we need to create the illusion that the item isn't added back to the folder yet, to
     // to correspond to the animation of the icon back into the folder. This is
-    public void hideItem(ShortcutInfo info) {
+    public void hideItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
         v.setVisibility(INVISIBLE);
     }
-    public void showItem(ShortcutInfo info) {
+    public void showItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
         v.setVisibility(VISIBLE);
     }
 
     @Override
-    public void onAdd(ShortcutInfo item, int rank) {
+    public void onAdd(WorkspaceItemInfo item, int rank) {
         View view = mContent.createAndAddViewForRank(item, rank);
         mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
@@ -1215,7 +1215,7 @@
         mItemsInvalidated = true;
     }
 
-    public void onRemove(ShortcutInfo item) {
+    public void onRemove(WorkspaceItemInfo item) {
         mItemsInvalidated = true;
         View v = getViewForInfo(item);
         mContent.removeItem(v);
@@ -1233,7 +1233,7 @@
         }
     }
 
-    private View getViewForInfo(final ShortcutInfo item) {
+    private View getViewForInfo(final WorkspaceItemInfo item) {
         return mContent.iterateOverItems(new ItemOperator() {
 
             @Override
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index bcd5701..02242a3 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -50,7 +50,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.Utilities;
@@ -226,15 +226,15 @@
         return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
-    public void addItem(ShortcutInfo item) {
+    public void addItem(WorkspaceItemInfo item) {
         addItem(item, true);
     }
 
-    public void addItem(ShortcutInfo item, boolean animate) {
+    public void addItem(WorkspaceItemInfo item, boolean animate) {
         mInfo.add(item, animate);
     }
 
-    public void removeItem(ShortcutInfo item, boolean animate) {
+    public void removeItem(WorkspaceItemInfo item, boolean animate) {
         mInfo.remove(item, animate);
     }
 
@@ -247,7 +247,7 @@
         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
         if (SPRING_LOADING_ENABLED &&
                 ((dragInfo instanceof AppInfo)
-                        || (dragInfo instanceof ShortcutInfo)
+                        || (dragInfo instanceof WorkspaceItemInfo)
                         || (dragInfo instanceof PendingAddShortcutInfo))) {
             mOpenAlarm.setAlarm(ON_OPEN_DELAY);
         }
@@ -264,8 +264,8 @@
         return mPreviewItemManager.prepareCreateAnimation(destView);
     }
 
-    public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
-            final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
+    public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView,
+            final WorkspaceItemInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer) {
         prepareCreateAnimation(destView);
         addItem(destInfo);
@@ -290,7 +290,7 @@
         mOpenAlarm.cancelAlarm();
     }
 
-    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
+    private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect,
             float scaleRelativeToDragLayer, int index,
             boolean itemReturnedOnFailedDrop) {
         item.cellX = -1;
@@ -382,15 +382,15 @@
     }
 
     public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
-        ShortcutInfo item;
+        WorkspaceItemInfo item;
         if (d.dragInfo instanceof AppInfo) {
             // Came from all apps -- make a copy
-            item = ((AppInfo) d.dragInfo).makeShortcut();
+            item = ((AppInfo) d.dragInfo).makeWorkspaceItem();
         } else if (d.dragSource instanceof BaseItemDragListener){
             // Came from a different window -- make a copy
-            item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
+            item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
         } else {
-            item = (ShortcutInfo) d.dragInfo;
+            item = (WorkspaceItemInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
         onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
@@ -581,7 +581,7 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item, int rank) {
+    public void onAdd(WorkspaceItemInfo item, int rank) {
         boolean wasDotted = mDotInfo.hasDot();
         mDotInfo.addDotInfo(mLauncher.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
@@ -591,7 +591,7 @@
     }
 
     @Override
-    public void onRemove(ShortcutInfo item) {
+    public void onRemove(WorkspaceItemInfo item) {
         boolean wasDotted = mDotInfo.hasDot();
         mDotInfo.subtractDotInfo(mLauncher.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index d4ce6dd..57105e7 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -38,7 +38,7 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.anim.Interpolators;
@@ -178,9 +178,9 @@
     /**
      * Binds items to the layout.
      */
-    public void bindItems(ArrayList<ShortcutInfo> items) {
+    public void bindItems(ArrayList<WorkspaceItemInfo> items) {
         ArrayList<View> icons = new ArrayList<>();
-        for (ShortcutInfo item : items) {
+        for (WorkspaceItemInfo item : items) {
             icons.add(createNewView(item));
         }
         arrangeChildren(icons, icons.size(), false);
@@ -203,7 +203,7 @@
         return rank;
     }
 
-    public View createAndAddViewForRank(ShortcutInfo item, int rank) {
+    public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) {
         View icon = createNewView(item);
         allocateSpaceForRank(rank);
         addViewForRank(icon, item, rank);
@@ -214,7 +214,7 @@
      * Adds the {@param view} to the layout based on {@param rank} and updated the position
      * related attributes. It assumes that {@param item} is already attached to the view.
      */
-    public void addViewForRank(View view, ShortcutInfo item, int rank) {
+    public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
         int pagePos = rank % mMaxItemsPerPage;
         int pageNo = rank / mMaxItemsPerPage;
 
@@ -229,10 +229,10 @@
     }
 
     @SuppressLint("InflateParams")
-    public View createNewView(ShortcutInfo item) {
+    public View createNewView(WorkspaceItemInfo item) {
         final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
                 R.layout.folder_application, null, false);
-        textView.applyFromShortcutInfo(item);
+        textView.applyFromWorkspaceItem(item);
         textView.setHapticFeedbackEnabled(false);
         textView.setOnClickListener(ItemClickHandler.INSTANCE);
         textView.setOnLongClickListener(mFolder);
@@ -631,7 +631,7 @@
             if (v != null) {
                 if (pageToAnimate != p) {
                     page.removeView(v);
-                    addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
+                    addViewForRank(v, (WorkspaceItemInfo) v.getTag(), moveStart);
                 } else {
                     // Do a fake animation before removing it.
                     final int newRank = moveStart;
@@ -644,7 +644,7 @@
                             mPendingAnimations.remove(v);
                             v.setTranslationX(oldTranslateX);
                             ((CellLayout) v.getParent().getParent()).removeView(v);
-                            addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
+                            addViewForRank(v, (WorkspaceItemInfo) v.getTag(), newRank);
                         }
                     };
                     v.animate()
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index af98680..49763ba 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -31,7 +31,7 @@
 import android.widget.TextView;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 
 import java.util.ArrayList;
@@ -301,7 +301,7 @@
      * @param dropped The item that was dropped onto the FolderIcon.
      */
     public void onDrop(List<BubbleTextView> oldParams, List<BubbleTextView> newParams,
-            ShortcutInfo dropped) {
+            WorkspaceItemInfo dropped) {
         int numItems = newParams.size();
         final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
         buildParamsForPage(0, params, false);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index eb45be1..d7b845b 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -21,7 +21,6 @@
 
 import android.annotation.TargetApi;
 import android.app.Fragment;
-import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
@@ -30,7 +29,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
 import android.os.Handler;
@@ -52,7 +50,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.allapps.SearchUiManager;
@@ -85,7 +83,7 @@
     private final DeviceProfile mDp;
     private final Rect mInsets;
 
-    private final ShortcutInfo mShortcutInfo;
+    private final WorkspaceItemInfo mWorkspaceItemInfo;
 
     public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp) {
         mUiHandler = new Handler(Looper.getMainLooper());
@@ -106,10 +104,10 @@
                 Process.myUserHandle(),
                 Build.VERSION.SDK_INT);
 
-        mShortcutInfo = new ShortcutInfo();
-        mShortcutInfo.applyFrom(iconInfo);
-        mShortcutInfo.intent = new Intent();
-        mShortcutInfo.contentDescription = mShortcutInfo.title =
+        mWorkspaceItemInfo = new WorkspaceItemInfo();
+        mWorkspaceItemInfo.applyFrom(iconInfo);
+        mWorkspaceItemInfo.intent = new Intent();
+        mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
                 context.getString(R.string.label_application);
     }
 
@@ -217,10 +215,10 @@
             return mWorkspace;
         }
 
-        private void inflateAndAddIcon(ShortcutInfo info) {
+        private void inflateAndAddIcon(WorkspaceItemInfo info) {
             BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
                     R.layout.app_icon, mWorkspace, false);
-            icon.applyFromShortcutInfo(info);
+            icon.applyFromWorkspaceItem(info);
             addInScreenFromBind(icon, info);
         }
 
@@ -245,7 +243,7 @@
         private void renderScreenShot(Canvas canvas) {
             // Add hotseat icons
             for (int i = 0; i < mIdp.numHotseatIcons; i++) {
-                ShortcutInfo info = new ShortcutInfo(mShortcutInfo);
+                WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
                 info.container = Favorites.CONTAINER_HOTSEAT;
                 info.screenId = i;
                 inflateAndAddIcon(info);
@@ -253,7 +251,7 @@
 
             // Add workspace icons
             for (int i = 0; i < mIdp.numColumns; i++) {
-                ShortcutInfo info = new ShortcutInfo(mShortcutInfo);
+                WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
                 info.container = Favorites.CONTAINER_DESKTOP;
                 info.screenId = 0;
                 info.cellX = i;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index fb0a367..648445e 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -36,7 +36,7 @@
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -130,7 +130,7 @@
         IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
             @Override
             public void run() {
-                if (info instanceof AppInfo || info instanceof ShortcutInfo) {
+                if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
                     getTitleAndIcon(info, false);
                 } else if (info instanceof PackageItemInfo) {
                     getTitleAndIconForApp((PackageItemInfo) info, false);
@@ -199,7 +199,7 @@
     }
 
     /**
-     * Fill in {@param shortcutInfo} with the icon and label for {@param info}
+     * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
      */
     private synchronized void getTitleAndIcon(
             @NonNull ItemInfoWithIcon infoInOut,
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 75f76d9..5c4af1f 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
@@ -28,9 +29,9 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.Themes;
 
 import java.util.function.Supplier;
@@ -43,6 +44,8 @@
  */
 public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
 
+    private static final String EXTRA_BADGEPKG = "badge_package";
+
     private static final Object sPoolSync = new Object();
     private static LauncherIcons sPool;
     private static int sPoolId = 0;
@@ -106,15 +109,15 @@
 
     // below methods should also migrate to BaseIconFactory
 
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) {
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
         return createShortcutIcon(shortcutInfo, true /* badged */);
     }
 
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged) {
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
         return createShortcutIcon(shortcutInfo, badged, null);
     }
 
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo,
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo,
             boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
         Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
                 .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
@@ -155,9 +158,9 @@
         return result;
     }
 
-    public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
+    public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
         ComponentName cn = shortcutInfo.getActivity();
-        String badgePkg = shortcutInfo.getBadgePackage(mContext);
+        String badgePkg = getBadgePackage(shortcutInfo);
         boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
         if (cn != null && !hasBadgePkgSet) {
             // Get the app info for the source activity.
@@ -175,4 +178,13 @@
             return pkgInfo;
         }
     }
+
+    private String getBadgePackage(ShortcutInfo si) {
+        String whitelistedPkg = mContext.getString(R.string.shortcutinfo_badgepkg_whitelist);
+        if (whitelistedPkg.equals(si.getPackage())
+                && si.getExtras().containsKey(EXTRA_BADGEPKG)) {
+            return si.getExtras().getString(EXTRA_BADGEPKG);
+        }
+        return si.getPackage();
+    }
 }
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index d208077..1ffa698 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -41,7 +41,6 @@
 /**
  * Helper methods for logging.
  */
-@Deprecated
 public class LoggerUtils {
     private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
     private static final String UNKNOWN = "UNKNOWN";
@@ -77,10 +76,17 @@
                 if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
                     str += " direction=" + getFieldName(action.dir, Action.Direction.class);
                 }
-                return str;
-            case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
+                break;
+            case Action.Type.COMMAND:
+                str += getFieldName(action.command, Action.Command.class);
+                break;
             default: return getFieldName(action.type, Action.Type.class);
         }
+        if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING ||
+                (action.command == Action.Command.BACK && action.dir != Action.Direction.NONE)) {
+            str += " direction=" + getFieldName(action.dir, Action.Direction.class);
+        }
+        return str;
     }
 
     public static String getTargetStr(Target t) {
@@ -102,13 +108,17 @@
                         t.containerType == NAVBAR) {
                     str += " id=" + t.pageIndex;
                 } else if (t.containerType == ContainerType.FOLDER) {
-                    str += " grid(" + t.gridX + "," + t.gridY+ ")";
+                    str += " grid(" + t.gridX + "," + t.gridY + ")";
                 }
                 break;
             default:
                 str += "UNKNOWN TARGET TYPE";
         }
 
+        if (t.spanX != 1 || t.spanY != 1) {
+            str += " span(" + t.spanX + "," + t.spanY + ")";
+        }
+
         if (t.tipType != TipType.DEFAULT_NONE) {
             str += " " + getFieldName(t.tipType, TipType.class);
         }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index c8a4e2a..bd785a1 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -96,7 +96,6 @@
      * Fills in the container data on the given event if the given view is not null.
      * @return whether container data was added.
      */
-    @Deprecated
     public static boolean fillInLogContainerData(LauncherLogProto.LauncherEvent event, @Nullable View v) {
         // Fill in grid(x,y), pageIndex of the child and container type of the parent
         LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
@@ -293,7 +292,7 @@
      * (1) WORKSPACE: if the launcher is the foreground activity
      * (2) APP: if another app was the foreground activity
      */
-    public void logStateChangeAction(int action, int dir, int srcChildTargetType,
+    public void logStateChangeAction(int action, int dir, int downX, int downY, int srcChildTargetType,
                                      int srcParentContainerType, int dstContainerType,
                                      int pageIndex) {
         LauncherEvent event;
@@ -311,6 +310,8 @@
         event.action.dir = dir;
         event.action.isStateChange = true;
         event.srcTarget[0].pageIndex = pageIndex;
+        event.srcTarget[0].spanX = downX;
+        event.srcTarget[0].spanY = downY;
         dispatchUserEvent(event, null);
         resetElapsedContainerMillis("state changed");
     }
@@ -325,7 +326,7 @@
 
     public void logDeepShortcutsOpen(View icon) {
         LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
-        if (icon == null || !(icon.getTag() instanceof ItemInfo)) {
+        if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
             return;
         }
         ItemInfo info = (ItemInfo) icon.getTag();
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 0d55301..ed0d470 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
@@ -77,7 +77,7 @@
 
                 if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                     if (item instanceof AppInfo) {
-                        item = ((AppInfo) item).makeShortcut();
+                        item = ((AppInfo) item).makeWorkspaceItem();
                     }
                 }
                 if (item != null) {
@@ -92,11 +92,11 @@
                 int screenId = coords[0];
 
                 ItemInfo itemInfo;
-                if (item instanceof ShortcutInfo || item instanceof FolderInfo ||
+                if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo ||
                         item instanceof LauncherAppWidgetInfo) {
                     itemInfo = item;
                 } else if (item instanceof AppInfo) {
-                    itemInfo = ((AppInfo) item).makeShortcut();
+                    itemInfo = ((AppInfo) item).makeWorkspaceItem();
                 } else {
                     throw new RuntimeException("Unexpected info type");
                 }
@@ -106,7 +106,7 @@
                         LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
                         coords[1], coords[2]);
 
-                // Save the ShortcutInfo for binding in the workspace
+                // Save the WorkspaceItemInfo for binding in the workspace
                 addedItemsFinal.add(itemInfo);
             }
         }
@@ -165,8 +165,8 @@
         boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
         synchronized (dataModel) {
             for (ItemInfo item : dataModel.itemsIdMap) {
-                if (item instanceof ShortcutInfo) {
-                    ShortcutInfo info = (ShortcutInfo) item;
+                if (item instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
                     if (item.getIntent() != null && info.user.equals(user)) {
                         Intent copyIntent = new Intent(item.getIntent());
                         copyIntent.setSourceBounds(intent.getSourceBounds());
@@ -178,7 +178,7 @@
                         // checking for existing promise icon with same package name
                         if (isLauncherAppTarget
                                 && info.isPromise()
-                                && info.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)
+                                && info.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
                                 && info.getTargetComponent() != null
                                 && compPkgName != null
                                 && compPkgName.equals(info.getTargetComponent().getPackageName())) {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index c9d8e3e..eea3d8c 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.model;
 
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
@@ -24,7 +23,7 @@
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.widget.WidgetListRowEntry;
@@ -94,13 +93,12 @@
     }
 
 
-    public void bindUpdatedShortcuts(
-            final ArrayList<ShortcutInfo> updatedShortcuts, final UserHandle user) {
+    public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
         if (!updatedShortcuts.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    callbacks.bindShortcutsChanged(updatedShortcuts, user);
+                    callbacks.bindWorkspaceItemsChanged(updatedShortcuts);
                 }
             });
         }
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index e8cc8f9..8f0cd08 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -26,7 +27,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.DumpTargetWrapper;
@@ -34,7 +35,6 @@
 import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
 import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
@@ -185,7 +185,7 @@
             FolderInfo fInfo = folders.valueAt(i);
             dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
             dtw.writeToDumpTarget(fInfo);
-            for(ShortcutInfo sInfo: fInfo.contents) {
+            for(WorkspaceItemInfo sInfo: fInfo.contents) {
                 DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
                 child.writeToDumpTarget(sInfo);
                 dtw.add(child);
@@ -335,7 +335,7 @@
                             Log.e(TAG, msg);
                         }
                     } else {
-                        findOrMakeFolder(item.container).add((ShortcutInfo) item, false);
+                        findOrMakeFolder(item.container).add((WorkspaceItemInfo) item, false);
                     }
 
                 }
@@ -366,7 +366,7 @@
      * Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
      */
     public synchronized void updateDeepShortcutCounts(
-            String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts) {
+            String packageName, UserHandle user, List<ShortcutInfo> shortcuts) {
         if (packageName != null) {
             Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
             while (keysIter.hasNext()) {
@@ -379,7 +379,7 @@
         }
 
         // Now add the new shortcuts to the map.
-        for (ShortcutInfoCompat shortcut : shortcuts) {
+        for (ShortcutInfo shortcut : shortcuts) {
             boolean shouldShowInContainer = shortcut.isEnabled()
                     && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
             if (shouldShowInContainer) {
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index be83d36..7852444 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -20,13 +20,13 @@
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -55,11 +55,11 @@
 
         final ArrayList<AppInfo> updatedApps = new ArrayList<>();
 
-        ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+        ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
         synchronized (dataModel) {
             for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     ComponentName cn = si.getTargetComponent();
                     if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                             && isValidShortcut(si) && cn != null
@@ -71,7 +71,7 @@
             }
             apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
         }
-        bindUpdatedShortcuts(updatedShortcuts, mUser);
+        bindUpdatedWorkspaceItems(updatedShortcuts);
 
         if (!updatedApps.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
@@ -83,7 +83,7 @@
         }
     }
 
-    public boolean isValidShortcut(ShortcutInfo si) {
+    public boolean isValidShortcut(WorkspaceItemInfo si) {
         switch (mOp) {
             case OP_CACHE_UPDATE:
                 return true;
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 88193d0..1a03b77 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -33,12 +33,12 @@
 import android.util.LongSparseArray;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -145,8 +145,8 @@
         }
     }
 
-    public ShortcutInfo loadSimpleShortcut() {
-        final ShortcutInfo info = new ShortcutInfo();
+    public WorkspaceItemInfo loadSimpleWorkspaceItem() {
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         // Non-app shortcuts are only supported for current user.
         info.user = user;
         info.itemType = itemType;
@@ -164,7 +164,7 @@
     /**
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
-    protected boolean loadIcon(ShortcutInfo info) {
+    protected boolean loadIcon(WorkspaceItemInfo info) {
         try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
             return loadIcon(info, li);
         }
@@ -173,7 +173,7 @@
     /**
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
-    protected boolean loadIcon(ShortcutInfo info, LauncherIcons li) {
+    protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
         if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
             String packageName = getString(iconPackageIndex);
             String resourceName = getString(iconResourceIndex);
@@ -209,11 +209,11 @@
     }
 
     /**
-     * Make an ShortcutInfo object for a restored application or shortcut item that points
+     * Make an WorkspaceItemInfo object for a restored application or shortcut item that points
      * to a package that is not yet installed on the system.
      */
-    public ShortcutInfo getRestoredItemInfo(Intent intent) {
-        final ShortcutInfo info = new ShortcutInfo();
+    public WorkspaceItemInfo getRestoredItemInfo(Intent intent) {
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.user = user;
         info.intent = intent;
 
@@ -222,12 +222,12 @@
             mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
         }
 
-        if (hasRestoreFlag(ShortcutInfo.FLAG_RESTORED_ICON)) {
+        if (hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORED_ICON)) {
             String title = getTitle();
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
             }
-        } else if  (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
+        } else if  (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
             if (TextUtils.isEmpty(info.title)) {
                 info.title = getTitle();
             }
@@ -242,9 +242,9 @@
     }
 
     /**
-     * Make an ShortcutInfo object for a shortcut that is an application.
+     * Make an WorkspaceItemInfo object for a shortcut that is an application.
      */
-    public ShortcutInfo getAppShortcutInfo(
+    public WorkspaceItemInfo getAppShortcutInfo(
             Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
             Log.d(TAG, "Null user found in getShortcutInfo");
@@ -267,7 +267,7 @@
             return null;
         }
 
-        final ShortcutInfo info = new ShortcutInfo();
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
         info.user = user;
         info.intent = newIntent;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index cc8e92f..0138572 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
 
 import android.appwidget.AppWidgetProviderInfo;
@@ -31,6 +30,7 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
@@ -49,7 +49,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -67,7 +67,6 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.provider.ImportDataTask;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LooperIdleLock;
@@ -286,7 +285,7 @@
                     mPackageInstaller.updateAndGetActiveSessionCache();
             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
 
-            Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+            Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
             final LoaderCursor c = new LoaderCursor(contentResolver.query(
                     LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
 
@@ -318,10 +317,10 @@
 
                     // We can only query for shortcuts when the user is unlocked.
                     if (userUnlocked) {
-                        List<ShortcutInfoCompat> pinnedShortcuts =
+                        List<ShortcutInfo> pinnedShortcuts =
                                 mShortcutManager.queryForPinnedShortcuts(null, user);
                         if (mShortcutManager.wasLastCallSuccess()) {
-                            for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+                            for (ShortcutInfo shortcut : pinnedShortcuts) {
                                 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
                                         shortcut);
                             }
@@ -335,7 +334,7 @@
                     unlockedUsers.put(serialNo, userUnlocked);
                 }
 
-                ShortcutInfo info;
+                WorkspaceItemInfo info;
                 LauncherAppWidgetInfo appWidgetInfo;
                 Intent intent;
                 String targetPkg;
@@ -360,7 +359,7 @@
                             }
 
                             int disabledState = quietMode.get(c.serialNumber) ?
-                                    ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+                                    WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
                             ComponentName cn = intent.getComponent();
                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
 
@@ -420,11 +419,11 @@
                                     // installed later.
                                     FileLog.d(TAG, "package not yet restored: " + targetPkg);
 
-                                    if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
+                                    if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
                                         // Restore has started once.
                                     } else if (installingPkgs.containsKey(targetPkg)) {
                                         // App restore has started. Update the flag
-                                        c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
+                                        c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
                                         c.updater().put(LauncherSettings.Favorites.RESTORED,
                                                 c.restoreFlag).commit();
                                     } else {
@@ -433,7 +432,7 @@
                                     }
                                 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
                                     // Package is present but not available.
-                                    disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+                                    disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE;
                                     // Add the icon on the workspace anyway.
                                     allowMissingTarget = true;
                                 } else if (!isSdCardReady) {
@@ -450,7 +449,7 @@
                                 }
                             }
 
-                            if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
+                            if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
                                 validTarget = false;
                             }
 
@@ -474,15 +473,15 @@
 
                                 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
                                 if (unlockedUsers.get(c.serialNumber)) {
-                                    ShortcutInfoCompat pinnedShortcut =
+                                    ShortcutInfo pinnedShortcut =
                                             shortcutKeyToPinnedShortcuts.get(key);
                                     if (pinnedShortcut == null) {
                                         // The shortcut is no longer valid.
                                         c.markDeleted("Pinned shortcut not found");
                                         continue;
                                     }
-                                    info = new ShortcutInfo(pinnedShortcut, context);
-                                    final ShortcutInfo finalInfo = info;
+                                    info = new WorkspaceItemInfo(pinnedShortcut, context);
+                                    final WorkspaceItemInfo finalInfo = info;
 
                                     LauncherIcons li = LauncherIcons.obtain(context);
                                     // If the pinned deep shortcut is no longer published,
@@ -499,11 +498,11 @@
                                     intent = info.intent;
                                 } else {
                                     // Create a shortcut info in disabled mode for now.
-                                    info = c.loadSimpleShortcut();
+                                    info = c.loadSimpleWorkspaceItem();
                                     info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                                 }
                             } else { // item type == ITEM_TYPE_SHORTCUT
-                                info = c.loadSimpleShortcut();
+                                info = c.loadSimpleWorkspaceItem();
 
                                 // Shortcuts are only available on the primary profile
                                 if (!TextUtils.isEmpty(targetPkg)
@@ -539,7 +538,7 @@
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                     SessionInfo si = installingPkgs.get(targetPkg);
                                     if (si == null) {
-                                        info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                        info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
                                     } else {
                                         info.setInstallProgress((int) (si.getProgress() * 100));
                                     }
@@ -547,7 +546,7 @@
 
                                 c.checkAndAddItem(info, mBgDataModel);
                             } else {
-                                throw new RuntimeException("Unexpected null ShortcutInfo");
+                                throw new RuntimeException("Unexpected null WorkspaceItemInfo");
                             }
                             break;
 
@@ -755,7 +754,7 @@
                 // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
                 // for now. Database will be updated once user manually modifies folder.
                 for (int rank = 0; rank < size; ++rank) {
-                    ShortcutInfo info = folder.contents.get(rank);
+                    WorkspaceItemInfo info = folder.contents.get(rank);
                     info.rank = rank;
 
                     if (info.usingLowResIcon()
@@ -782,8 +781,8 @@
         HashSet<String> packagesToIgnore = new HashSet<>();
         synchronized (mBgDataModel) {
             for (ItemInfo info : mBgDataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                if (info instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     if (si.isPromise() && si.getTargetComponent() != null) {
                         packagesToIgnore.add(si.getTargetComponent().getPackageName());
                     }
@@ -840,7 +839,7 @@
         if (mBgDataModel.hasShortcutHostPermission) {
             for (UserHandle user : mUserManager.getUserProfiles()) {
                 if (mUserManager.isUserUnlocked(user)) {
-                    List<ShortcutInfoCompat> shortcuts =
+                    List<ShortcutInfo> shortcuts =
                             mShortcutManager.queryForAllShortcuts(user);
                     mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
                 }
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index daf99e9..40c914b 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -36,7 +36,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ContentWriter;
@@ -114,7 +114,7 @@
         if (modelItem != null && item != modelItem) {
             // check all the data is consistent
             if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
-                    modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+                    modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
                 if (modelItem.title.toString().equals(item.title.toString()) &&
                         modelItem.getIntent().filterEquals(item.getIntent()) &&
                         modelItem.id == item.id &&
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 32dfe25..5f6d128 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.PromiseAppInfo;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
@@ -106,15 +106,15 @@
         synchronized (dataModel) {
             final HashSet<ItemInfo> updates = new HashSet<>();
             for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                if (info instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     ComponentName cn = si.getTargetComponent();
                     if (si.hasPromiseIconUi() && (cn != null)
                             && mInstallInfo.packageName.equals(cn.getPackageName())) {
                         si.setInstallProgress(mInstallInfo.progress);
                         if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
                             // Mark this info as broken.
-                            si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                            si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
                         }
                         updates.add(si);
                     }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 0f67f0c..c37ed99 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,12 +18,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
@@ -33,7 +35,6 @@
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -42,7 +43,6 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -108,7 +108,7 @@
                         SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
                     }
                 }
-                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -119,7 +119,7 @@
                     app.getWidgetCache().removePackage(packages[i], mUser);
                 }
                 // Since package was just updated, the target must be available now.
-                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
@@ -133,20 +133,20 @@
                     appsList.removePackage(packages[i], mUser);
                     app.getWidgetCache().removePackage(packages[i], mUser);
                 }
-                flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_SUSPEND:
             case OP_UNSUSPEND:
                 flagOp = mOp == OP_SUSPEND ?
-                        FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
-                        FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
+                        FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) :
+                        FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
                 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
                 appsList.updateDisabledFlags(matcher, flagOp);
                 break;
             case OP_USER_AVAILABILITY_CHANGE:
                 flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
-                        ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
-                        : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
+                        ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
+                        : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
                 // We want to update all packages for this user.
                 matcher = ItemInfoMatcher.ofUser(mUser);
                 appsList.updateDisabledFlags(matcher, flagOp);
@@ -175,15 +175,15 @@
 
         // Update shortcut infos
         if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
-            final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+            final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
             final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
 
             // For system apps, package manager send OP_UPDATE when an app is enabled.
             final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
             synchronized (dataModel) {
                 for (ItemInfo info : dataModel.itemsIdMap) {
-                    if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
-                        ShortcutInfo si = (ShortcutInfo) info;
+                    if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
+                        WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                         boolean infoUpdated = false;
                         boolean shortcutUpdated = false;
 
@@ -203,7 +203,7 @@
                         if (cn != null && matcher.matches(si, cn)) {
                             String packageName = cn.getPackageName();
 
-                            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
+                            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
                                 removedShortcuts.put(si.id, false);
                                 if (mOp == OP_REMOVE) {
                                     continue;
@@ -213,7 +213,7 @@
                             if (si.isPromise() && isNewApkAvailable) {
                                 boolean isTargetValid = true;
                                 if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                                    List<ShortcutInfoCompat> shortcut = DeepShortcutManager
+                                    List<ShortcutInfo> shortcut = DeepShortcutManager
                                             .getInstance(context).queryForPinnedShortcuts(
                                                     cn.getPackageName(),
                                                     Arrays.asList(si.getDeepShortcutId()), mUser);
@@ -227,9 +227,9 @@
                                     isTargetValid = LauncherAppsCompat.getInstance(context)
                                             .isActivityEnabledForProfile(cn, mUser);
                                 }
-                                if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)
+                                if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
                                         && !isTargetValid) {
-                                    if (updateShortcutIntent(context, si, packageName)) {
+                                    if (updateWorkspaceItemIntent(context, si, packageName)) {
                                         infoUpdated = true;
                                     } else if (si.hasPromiseIconUi()) {
                                         removedShortcuts.put(si.id, true);
@@ -241,11 +241,11 @@
                                             + si.intent);
                                     continue;
                                 } else {
-                                    si.status = ShortcutInfo.DEFAULT;
+                                    si.status = WorkspaceItemInfo.DEFAULT;
                                     infoUpdated = true;
                                 }
                             } else if (isNewApkAvailable && removedComponents.contains(cn)) {
-                                if (updateShortcutIntent(context, si, packageName)) {
+                                if (updateWorkspaceItemIntent(context, si, packageName)) {
                                     infoUpdated = true;
                                 }
                             }
@@ -264,7 +264,7 @@
                         }
 
                         if (infoUpdated || shortcutUpdated) {
-                            updatedShortcuts.add(si);
+                            updatedWorkspaceItems.add(si);
                         }
                         if (infoUpdated) {
                             getModelWriter().updateItemInDatabase(si);
@@ -290,7 +290,7 @@
                 }
             }
 
-            bindUpdatedShortcuts(updatedShortcuts, mUser);
+            bindUpdatedWorkspaceItems(updatedWorkspaceItems);
             if (!removedShortcuts.isEmpty()) {
                 deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
             }
@@ -356,12 +356,13 @@
      * Updates {@param si}'s intent to point to a new ComponentName.
      * @return Whether the shortcut intent was changed.
      */
-    private boolean updateShortcutIntent(Context context, ShortcutInfo si, String packageName) {
+    private boolean updateWorkspaceItemIntent(Context context,
+            WorkspaceItemInfo si, String packageName) {
         // Try to find the best match activity.
         Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
         if (intent != null) {
             si.intent = intent;
-            si.status = ShortcutInfo.DEFAULT;
+            si.status = WorkspaceItemInfo.DEFAULT;
             return true;
         }
         return false;
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1644c89..8528228 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -16,16 +16,16 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
@@ -40,11 +40,11 @@
 public class ShortcutsChangedTask extends BaseModelUpdateTask {
 
     private final String mPackageName;
-    private final List<ShortcutInfoCompat> mShortcuts;
+    private final List<ShortcutInfo> mShortcuts;
     private final UserHandle mUser;
     private final boolean mUpdateIdMap;
 
-    public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user, boolean updateIdMap) {
         mPackageName = packageName;
         mShortcuts = shortcuts;
@@ -56,16 +56,15 @@
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         final Context context = app.getContext();
         DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
-        deepShortcutManager.onShortcutsChanged(mShortcuts);
 
-        // Find ShortcutInfo's that have changed on the workspace.
+        // Find WorkspaceItemInfo's that have changed on the workspace.
         HashSet<ShortcutKey> removedKeys = new HashSet<>();
-        MultiHashMap<ShortcutKey, ShortcutInfo> keyToShortcutInfo = new MultiHashMap<>();
+        MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
         HashSet<String> allIds = new HashSet<>();
 
         for (ItemInfo itemInfo : dataModel.itemsIdMap) {
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                ShortcutInfo si = (ShortcutInfo) itemInfo;
+                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
                 if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
                     keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
                     allIds.add(si.getDeepShortcutId());
@@ -73,14 +72,14 @@
             }
         }
 
-        final ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
         if (!keyToShortcutInfo.isEmpty()) {
             // Update the workspace to reflect the changes to updated shortcuts residing on it.
-            List<ShortcutInfoCompat> shortcuts = deepShortcutManager.queryForFullDetails(
+            List<ShortcutInfo> shortcuts = deepShortcutManager.queryForFullDetails(
                     mPackageName, new ArrayList<>(allIds), mUser);
-            for (ShortcutInfoCompat fullDetails : shortcuts) {
+            for (ShortcutInfo fullDetails : shortcuts) {
                 ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
-                List<ShortcutInfo> shortcutInfos = keyToShortcutInfo.remove(key);
+                List<WorkspaceItemInfo> workspaceItemInfos = keyToShortcutInfo.remove(key);
                 if (!fullDetails.isPinned()) {
                     // The shortcut was previously pinned but is no longer, so remove it from
                     // the workspace and our pinned shortcut counts.
@@ -90,15 +89,15 @@
                     removedKeys.add(key);
                     continue;
                 }
-                for (final ShortcutInfo shortcutInfo : shortcutInfos) {
-                    shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
+                for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
+                    workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
                     // If the shortcut is pinned but no longer has an icon in the system,
                     // keep the current icon instead of reverting to the default icon.
                     LauncherIcons li = LauncherIcons.obtain(context);
-                    shortcutInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
-                            () -> shortcutInfo));
+                    workspaceItemInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
+                            () -> workspaceItemInfo));
                     li.recycle();
-                    updatedShortcutInfos.add(shortcutInfo);
+                    updatedWorkspaceItemInfos.add(workspaceItemInfo);
                 }
             }
         }
@@ -108,7 +107,7 @@
         // means they were cleared, so we remove and unpin them now.
         removedKeys.addAll(keyToShortcutInfo.keySet());
 
-        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!keyToShortcutInfo.isEmpty()) {
             deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 7c4e454..2cb256e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -18,17 +18,17 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -56,12 +56,12 @@
         boolean isUserUnlocked = UserManagerCompat.getInstance(context).isUserUnlocked(mUser);
         DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
 
-        HashMap<ShortcutKey, ShortcutInfoCompat> pinnedShortcuts = new HashMap<>();
+        HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
         if (isUserUnlocked) {
-            List<ShortcutInfoCompat> shortcuts =
+            List<ShortcutInfo> shortcuts =
                     deepShortcutManager.queryForPinnedShortcuts(null, mUser);
             if (deepShortcutManager.wasLastCallSuccess()) {
-                for (ShortcutInfoCompat shortcut : shortcuts) {
+                for (ShortcutInfo shortcut : shortcuts) {
                     pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
                 }
             } else {
@@ -73,16 +73,16 @@
         }
 
         // Update the workspace to reflect the changes to updated shortcuts residing on it.
-        ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+        ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
         HashSet<ShortcutKey> removedKeys = new HashSet<>();
 
         for (ItemInfo itemInfo : dataModel.itemsIdMap) {
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                     && mUser.equals(itemInfo.user)) {
-                ShortcutInfo si = (ShortcutInfo) itemInfo;
+                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
                 if (isUserUnlocked) {
                     ShortcutKey key = ShortcutKey.fromItemInfo(si);
-                    ShortcutInfoCompat shortcut = pinnedShortcuts.get(key);
+                    ShortcutInfo shortcut = pinnedShortcuts.get(key);
                     // We couldn't verify the shortcut during loader. If its no longer available
                     // (probably due to clear data), delete the workspace item as well
                     if (shortcut == null) {
@@ -99,10 +99,10 @@
                 } else {
                     si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                 }
-                updatedShortcutInfos.add(si);
+                updatedWorkspaceItemInfos.add(si);
             }
         }
-        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!removedKeys.isEmpty()) {
             deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 2c59202..dbfe988 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -17,19 +17,19 @@
 package com.android.launcher3.popup;
 
 import android.content.ComponentName;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
@@ -54,10 +54,10 @@
     /**
      * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
      */
-    private static final Comparator<ShortcutInfoCompat> SHORTCUT_RANK_COMPARATOR
-            = new Comparator<ShortcutInfoCompat>() {
+    private static final Comparator<ShortcutInfo> SHORTCUT_RANK_COMPARATOR
+            = new Comparator<ShortcutInfo>() {
         @Override
-        public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
+        public int compare(ShortcutInfo a, ShortcutInfo b) {
             if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
                 return -1;
             }
@@ -76,11 +76,11 @@
      * @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
      * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
      */
-    public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
-            List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
+    public static List<ShortcutInfo> sortAndFilterShortcuts(
+            List<ShortcutInfo> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
         // Remove up to one specific shortcut before sorting and doing somewhat fancy filtering.
         if (shortcutIdToRemoveFirst != null) {
-            Iterator<ShortcutInfoCompat> shortcutIterator = shortcuts.iterator();
+            Iterator<ShortcutInfo> shortcutIterator = shortcuts.iterator();
             while (shortcutIterator.hasNext()) {
                 if (shortcutIterator.next().getId().equals(shortcutIdToRemoveFirst)) {
                     shortcutIterator.remove();
@@ -96,11 +96,11 @@
 
         // The list of shortcuts is now sorted with static shortcuts followed by dynamic
         // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
-        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
+        List<ShortcutInfo> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
         int numDynamic = 0;
         int size = shortcuts.size();
         for (int i = 0; i < size; i++) {
-            ShortcutInfoCompat shortcut = shortcuts.get(i);
+            ShortcutInfo shortcut = shortcuts.get(i);
             int filteredSize = filteredShortcuts.size();
             if (filteredSize < MAX_SHORTCUTS) {
                 // Always add the first MAX_SHORTCUTS to the filtered list.
@@ -140,14 +140,14 @@
                 uiHandler.post(() -> container.applyNotificationInfos(infos));
             }
 
-            List<ShortcutInfoCompat> shortcuts = DeepShortcutManager.getInstance(launcher)
+            List<ShortcutInfo> shortcuts = DeepShortcutManager.getInstance(launcher)
                     .queryForShortcutsContainer(activity, user);
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
             for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
-                final ShortcutInfoCompat shortcut = shortcuts.get(i);
-                final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
+                final ShortcutInfo shortcut = shortcuts.get(i);
+                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
                 // Use unbadged icon for the menu.
                 LauncherIcons li = LauncherIcons.obtain(launcher);
                 si.applyFrom(li.createShortcutIcon(shortcut, false /* badged */));
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index e7b8292..563f3b3 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -3,6 +3,7 @@
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 
+import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
@@ -20,7 +21,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -159,9 +160,8 @@
             return (view) -> {
                 dismissTaskMenuView(activity);
                 Rect sourceBounds = activity.getViewBounds(view);
-                Bundle opts = activity.getActivityLaunchOptionsAsBundle(view);
                 new PackageManagerHelper(activity).startDetailsActivityForInfo(
-                        itemInfo, sourceBounds, opts);
+                        itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
                 activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                         ControlType.APPINFO_TARGET, view);
             };
@@ -176,8 +176,8 @@
         @Override
         public View.OnClickListener getOnClickListener(
                 BaseDraggingActivity activity, ItemInfo itemInfo) {
-            boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) &&
-                    ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI);
+            boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
+                    ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
             boolean isInstantApp = false;
             if (itemInfo instanceof com.android.launcher3.AppInfo) {
                 com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 040b5e5..e6e20e1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -31,7 +31,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -110,8 +110,8 @@
         // Mark all items as restored.
         boolean keepAllIcons = Utilities.isPropertyEnabled(LogConfig.KEEP_ALL_ICONS);
         ContentValues values = new ContentValues();
-        values.put(Favorites.RESTORED, ShortcutInfo.FLAG_RESTORED_ICON
-                | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
+        values.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_RESTORED_ICON
+                | (keepAllIcons ? WorkspaceItemInfo.FLAG_RESTORE_STARTED : 0));
         db.update(Favorites.TABLE_NAME, values, null, null);
 
         // Mark widgets with appropriate restore flag.
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 7b93ba2..9274d44 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,8 +17,8 @@
 package com.android.launcher3.shortcuts;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -27,10 +27,9 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.touch.ItemClickHandler;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -44,8 +43,8 @@
     private View mIconView;
     private View mDivider;
 
-    private ShortcutInfo mInfo;
-    private ShortcutInfoCompat mDetail;
+    private WorkspaceItemInfo mInfo;
+    private ShortcutInfo mDetail;
 
     public DeepShortcutView(Context context) {
         this(context, null, 0);
@@ -95,11 +94,11 @@
     }
 
     /** package private **/
-    public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
+    public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail,
             PopupContainerWithArrow container) {
         mInfo = info;
         mDetail = detail;
-        mBubbleText.applyFromShortcutInfo(info);
+        mBubbleText.applyFromWorkspaceItem(info);
         mIconView.setBackground(mBubbleText.getIcon());
 
         // Use the long label as long as it exists and fits.
@@ -119,12 +118,12 @@
     /**
      * Returns the shortcut info that is suitable to be added on the homescreen
      */
-    public ShortcutInfo getFinalInfo() {
-        final ShortcutInfo badged = new ShortcutInfo(mInfo);
+    public WorkspaceItemInfo getFinalInfo() {
+        final WorkspaceItemInfo badged = new WorkspaceItemInfo(mInfo);
         // Queue an update task on the worker thread. This ensures that the badged
         // shortcut eventually gets its icon updated.
         Launcher.getLauncher(getContext()).getModel()
-                .updateAndBindShortcutInfo(badged, mDetail);
+                .updateAndBindWorkspaceItem(badged, mDetail);
         return badged;
     }
 
@@ -132,7 +131,7 @@
         return mIconView;
     }
 
-    public ShortcutInfoCompat getDetail() {
+    public ShortcutInfo getDetail() {
         return mDetail;
     }
 }
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
deleted file mode 100644
index e5bd002..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2016 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.shortcuts;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.os.UserHandle;
-
-import com.android.launcher3.R;
-
-/**
- * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
- *
- * Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
- */
-public class ShortcutInfoCompat {
-    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
-    private static final String EXTRA_BADGEPKG = "badge_package";
-    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
-    private ShortcutInfo mShortcutInfo;
-
-    public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
-        mShortcutInfo = shortcutInfo;
-    }
-
-    public Intent makeIntent() {
-        return new Intent(Intent.ACTION_MAIN)
-                .addCategory(INTENT_CATEGORY)
-                .setComponent(getActivity())
-                .setPackage(getPackage())
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
-                .putExtra(EXTRA_SHORTCUT_ID, getId());
-    }
-
-    public ShortcutInfo getShortcutInfo() {
-        return mShortcutInfo;
-    }
-
-    public String getPackage() {
-        return mShortcutInfo.getPackage();
-    }
-
-    public String getBadgePackage(Context context) {
-        String whitelistedPkg = context.getString(R.string.shortcutinfocompat_badgepkg_whitelist);
-        if (whitelistedPkg.equals(getPackage())
-                && mShortcutInfo.getExtras().containsKey(EXTRA_BADGEPKG)) {
-            return mShortcutInfo.getExtras().getString(EXTRA_BADGEPKG);
-        }
-        return getPackage();
-    }
-
-    public String getId() {
-        return mShortcutInfo.getId();
-    }
-
-    public CharSequence getShortLabel() {
-        return mShortcutInfo.getShortLabel();
-    }
-
-    public CharSequence getLongLabel() {
-        return mShortcutInfo.getLongLabel();
-    }
-
-    public long getLastChangedTimestamp() {
-        return mShortcutInfo.getLastChangedTimestamp();
-    }
-
-    public ComponentName getActivity() {
-        return mShortcutInfo.getActivity();
-    }
-
-    public UserHandle getUserHandle() {
-        return mShortcutInfo.getUserHandle();
-    }
-
-    public boolean hasKeyFieldsOnly() {
-        return mShortcutInfo.hasKeyFieldsOnly();
-    }
-
-    public boolean isPinned() {
-        return mShortcutInfo.isPinned();
-    }
-
-    public boolean isDeclaredInManifest() {
-        return mShortcutInfo.isDeclaredInManifest();
-    }
-
-    public boolean isEnabled() {
-        return mShortcutInfo.isEnabled();
-    }
-
-    public boolean isDynamic() {
-        return mShortcutInfo.isDynamic();
-    }
-
-    public int getRank() {
-        return mShortcutInfo.getRank();
-    }
-
-    public CharSequence getDisabledMessage() {
-        return mShortcutInfo.getDisabledMessage();
-    }
-
-    @Override
-    public String toString() {
-        return mShortcutInfo.toString();
-    }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index cbef85a..70665ca 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -2,6 +2,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.ItemInfo;
@@ -12,6 +13,9 @@
  */
 public class ShortcutKey extends ComponentKey {
 
+    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
+    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+
     public ShortcutKey(String packageName, UserHandle user, String id) {
         // Use the id as the class name.
         super(new ComponentName(packageName, id), user);
@@ -25,18 +29,26 @@
         return componentName.getClassName();
     }
 
-    public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
+    public static ShortcutKey fromInfo(ShortcutInfo shortcutInfo) {
         return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
                 shortcutInfo.getId());
     }
 
     public static ShortcutKey fromIntent(Intent intent, UserHandle user) {
-        String shortcutId = intent.getStringExtra(
-                ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
+        String shortcutId = intent.getStringExtra(EXTRA_SHORTCUT_ID);
         return new ShortcutKey(intent.getPackage(), user, shortcutId);
     }
 
     public static ShortcutKey fromItemInfo(ItemInfo info) {
         return fromIntent(info.getIntent(), info.user);
     }
+
+    public static Intent makeIntent(ShortcutInfo si) {
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(INTENT_CATEGORY)
+                .setComponent(si.getActivity())
+                .setPackage(si.getPackage())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                .putExtra(EXTRA_SHORTCUT_ID, si.getId());
+    }
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 0274de3..a1871ff 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -540,7 +540,7 @@
     private void logReachedState(int logAction, LauncherState targetState) {
         // Transition complete. log the action
         mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                getDirectionForLog(),
+                getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
                 mStartContainerType,
                 mStartState.containerType,
                 targetState.containerType,
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 3c77860..026770c 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -86,8 +86,8 @@
         }
 
         Object tag = v.getTag();
-        if (tag instanceof ShortcutInfo) {
-            onClickAppShortcut(v, (ShortcutInfo) tag, launcher, sourceContainer);
+        if (tag instanceof WorkspaceItemInfo) {
+            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
         } else if (tag instanceof FolderInfo) {
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
@@ -176,12 +176,13 @@
     /**
      * Event handler for an app shortcut click.
      *
-     * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+     * @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
      */
-    public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher,
+    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher,
             @Nullable String sourceContainer) {
         if (shortcut.isDisabled()) {
-            final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+            final int disabledFlags = shortcut.runtimeStatusFlags
+                    & WorkspaceItemInfo.FLAG_DISABLED_MASK;
             if ((disabledFlags &
                     ~FLAG_DISABLED_SUSPENDED &
                     ~FLAG_DISABLED_QUIET_USER) == 0) {
@@ -212,7 +213,7 @@
                     shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
             if (!TextUtils.isEmpty(packageName)) {
                 onClickPendingAppItem(v, launcher, packageName,
-                        shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+                        shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
                 return;
             }
         }
@@ -233,9 +234,9 @@
         if (intent == null) {
             throw new IllegalArgumentException("Input must have a valid intent");
         }
-        if (item instanceof ShortcutInfo) {
-            ShortcutInfo si = (ShortcutInfo) item;
-            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
+        if (item instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo si = (WorkspaceItemInfo) item;
+            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
                     && Intent.ACTION_VIEW.equals(intent.getAction())) {
                 // make a copy of the intent that has the package set to null
                 // we do this because the platform sometimes disables instant
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index d758a29..4e3dcf8 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -180,6 +180,13 @@
         return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
     }
 
+    public int getDownX() {
+        return (int) mDownPos.x;
+    }
+
+    public int getDownY() {
+        return (int) mDownPos.y;
+    }
     /**
      * There's no touch and there's no animation.
      */
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 4de082e..07ddccb 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -20,12 +20,12 @@
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-import static android.view.ViewConfiguration.getLongPressTimeout;
 
 import static com.android.launcher3.LauncherState.NORMAL;
 
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.view.GestureDetector;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.View;
@@ -45,7 +45,8 @@
 /**
  * Helper class to handle touch on empty space in workspace and show options popup on long press
  */
-public class WorkspaceTouchListener implements OnTouchListener, Runnable {
+public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListener
+        implements OnTouchListener {
 
     /**
      * STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
@@ -66,16 +67,21 @@
 
     private int mLongPressState = STATE_CANCELLED;
 
+    private final GestureDetector mGestureDetector;
+
     public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
         mLauncher = launcher;
         mWorkspace = workspace;
         // Use twice the touch slop as we are looking for long press which is more
         // likely to cause movement.
         mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop();
+        mGestureDetector = new GestureDetector(workspace.getContext(), this);
     }
 
     @Override
     public boolean onTouch(View view, MotionEvent ev) {
+        mGestureDetector.onTouchEvent(ev);
+
         int action = ev.getActionMasked();
         if (action == ACTION_DOWN) {
             // Check if we can handle long press.
@@ -97,7 +103,6 @@
             if (handleLongPress) {
                 mLongPressState = STATE_REQUESTED;
                 mTouchDownPoint.set(ev.getX(), ev.getY());
-                mWorkspace.postDelayed(this, getLongPressTimeout());
             }
 
             mWorkspace.onTouchEvent(ev);
@@ -143,9 +148,6 @@
             }
         }
 
-        if (action == ACTION_UP || action == ACTION_CANCEL) {
-            cancelLongPress();
-        }
         return result;
     }
 
@@ -155,12 +157,11 @@
     }
 
     private void cancelLongPress() {
-        mWorkspace.removeCallbacks(this);
         mLongPressState = STATE_CANCELLED;
     }
 
     @Override
-    public void run() {
+    public void onLongPress(MotionEvent event) {
         if (mLongPressState == STATE_REQUESTED) {
             if (canHandleLongPress()) {
                 mLongPressState = STATE_PENDING_PARENT_INFORM;
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
index a012c86..bd40eb9 100644
--- a/src/com/android/launcher3/util/FlagOp.java
+++ b/src/com/android/launcher3/util/FlagOp.java
@@ -7,7 +7,7 @@
     int apply(int flags);
 
     static FlagOp addFlag(int flag) {
-        return i -> i + flag;
+        return i -> i | flag;
     }
 
     static FlagOp removeFlag(int flag) {
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index c3570fe..59a5ed6 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -23,7 +23,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.HashSet;
@@ -41,15 +41,15 @@
     default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
         HashSet<ItemInfo> filtered = new HashSet<>();
         for (ItemInfo i : infos) {
-            if (i instanceof ShortcutInfo) {
-                ShortcutInfo info = (ShortcutInfo) i;
+            if (i instanceof WorkspaceItemInfo) {
+                WorkspaceItemInfo info = (WorkspaceItemInfo) i;
                 ComponentName cn = info.getTargetComponent();
                 if (cn != null && matches(info, cn)) {
                     filtered.add(info);
                 }
             } else if (i instanceof FolderInfo) {
                 FolderInfo info = (FolderInfo) i;
-                for (ShortcutInfo s : info.contents) {
+                for (WorkspaceItemInfo s : info.contents) {
                     ComponentName cn = s.getTargetComponent();
                     if (cn != null && matches(s, cn)) {
                         filtered.add(s);
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 5747db1..2ee0328 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,6 +22,8 @@
 
 import java.util.concurrent.ExecutionException;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Utility class for defining singletons which are initiated on main thread.
  */
@@ -53,6 +55,11 @@
         return mValue;
     }
 
+    @VisibleForTesting
+    public void initializeForTesting(T value) {
+        mValue = value;
+    }
+
     public interface ObjectProvider<T> {
 
         T get(Context context);
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index d71bd15..7439ac1 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
@@ -30,6 +31,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.PatternMatcher;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -41,7 +43,7 @@
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 
 import java.net.URISyntaxException;
@@ -183,7 +185,7 @@
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
-        } else if (info instanceof ShortcutInfo) {
+        } else if (info instanceof WorkspaceItemInfo) {
             componentName = info.getTargetComponent();
         } else if (info instanceof PendingAddItemInfo) {
             componentName = ((PendingAddItemInfo) info).componentName;
@@ -200,4 +202,17 @@
             }
         }
     }
+
+    /**
+     * Creates an intent filter to listen for actions with a specific package in the data field.
+     */
+    public static IntentFilter getPackageFilter(String pkg, String... actions) {
+        IntentFilter packageFilter = new IntentFilter();
+        for (String action : actions) {
+            packageFilter.addAction(action);
+        }
+        packageFilter.addDataScheme("package");
+        packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
+        return packageFilter;
+    }
 }
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 59fd859..a45f17d 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -32,6 +32,14 @@
  */
 public class Themes {
 
+    public static String getDefaultBodyFont(Context context) {
+        TypedArray ta = context.obtainStyledAttributes(android.R.style.TextAppearance_DeviceDefault,
+                new int[]{android.R.attr.fontFamily});
+        String value = ta.getString(0);
+        ta.recycle();
+        return value;
+    }
+
     public static float getDialogCornerRadius(Context context) {
         return getDimension(context, android.R.attr.dialogCornerRadius,
                 context.getResources().getDimension(R.dimen.default_dialog_corner_radius));
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index fab21fa..f2fc718 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -70,7 +70,7 @@
 public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
 
     public static final float SHAPE_PROGRESS_DURATION = 0.15f;
-
+    private static final int FADE_DURATION_MS = 200;
     private static final Rect sTmpRect = new Rect();
 
     private Runnable mEndRunnable;
@@ -93,10 +93,15 @@
     private float mBgDrawableStartScale = 1f;
     private float mBgDrawableEndScale = 1f;
 
+    private AnimatorSet mFadeAnimatorSet;
+    private ListenerView mListenerView;
+
     private FloatingIconView(Context context) {
         super(context);
+
         mBlurSizeOutline = context.getResources().getDimensionPixelSize(
                 R.dimen.blur_size_medium_outline);
+        mListenerView = new ListenerView(context, null);
     }
 
     /**
@@ -138,6 +143,12 @@
             if (mRevealAnimator == null) {
                 mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
                         mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening);
+                mRevealAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mRevealAnimator = null;
+                    }
+                });
                 mRevealAnimator.start();
                 // We pause here so we can set the current fraction ourselves.
                 mRevealAnimator.pause();
@@ -314,7 +325,7 @@
 
     @WorkerThread
     private int getOffsetForIconBounds(Drawable drawable) {
-        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
                 !(drawable instanceof AdaptiveIconDrawable)) {
             return 0;
         }
@@ -364,6 +375,18 @@
         }
     }
 
+    public void onListenerViewClosed() {
+        // Fast finish here.
+        if (mEndRunnable != null) {
+            mEndRunnable.run();
+            mEndRunnable = null;
+        }
+        if (mFadeAnimatorSet != null) {
+            mFadeAnimatorSet.end();
+            mFadeAnimatorSet = null;
+        }
+    }
+
     @Override
     public void onAnimationStart(Animator animator) {}
 
@@ -377,6 +400,7 @@
      * Creates a floating icon view for {@param originalView}.
      * @param originalView The view to copy
      * @param hideOriginal If true, it will hide {@param originalView} while this view is visible.
+     *                     Else, we will not draw anything in this view.
      * @param positionOut Rect that will hold the size and position of v.
      * @param isOpening True if this view replaces the icon for app open animation.
      */
@@ -392,14 +416,12 @@
 
         // Get the drawable on the background thread
         // Must be called after matchPositionOf so that we know what size to load.
-        if (originalView.getTag() instanceof ItemInfo) {
+        if (originalView.getTag() instanceof ItemInfo && hideOriginal) {
             view.mLoadIconSignal = new CancellationSignal();
             Runnable onIconLoaded = () -> {
                 // Delay swapping views until the icon is loaded to prevent a flash.
                 view.setVisibility(VISIBLE);
-                if (hideOriginal) {
-                    originalView.setVisibility(INVISIBLE);
-                }
+                originalView.setVisibility(INVISIBLE);
             };
             CancellationSignal loadIconSignal = view.mLoadIconSignal;
             new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
@@ -411,58 +433,71 @@
         // We need to add it to the overlay, but keep it invisible until animation starts..
         final DragLayer dragLayer = launcher.getDragLayer();
         view.setVisibility(INVISIBLE);
-        ((ViewGroup) dragLayer.getParent()).getOverlay().add(view);
+        ((ViewGroup) dragLayer.getParent()).addView(view);
+        dragLayer.addView(view.mListenerView);
+        view.mListenerView.setListener(view::onListenerViewClosed);
 
-        if (hideOriginal) {
-            view.mEndRunnable = () -> {
-                AnimatorSet fade = new AnimatorSet();
-                fade.setDuration(200);
-                fade.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        originalView.setVisibility(VISIBLE);
+        view.mEndRunnable = () -> {
+            view.mEndRunnable = null;
 
-                        if (originalView instanceof FolderIcon) {
-                            FolderIcon folderIcon = (FolderIcon) originalView;
-                            folderIcon.setBackgroundVisible(false);
-                            folderIcon.getFolderName().setTextVisibility(false);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);
-
-                        if (view.mRevealAnimator != null) {
-                            view.mRevealAnimator.end();
-                        }
-                    }
-                });
-
-                if (originalView instanceof FolderIcon) {
-                    FolderIcon folderIcon = (FolderIcon) originalView;
-                    fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
-                    fade.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            folderIcon.setBackgroundVisible(true);
-                            folderIcon.animateBgShadowAndStroke();
-                            if (folderIcon.hasDot()) {
-                                folderIcon.animateDotScale(0, 1f);
-                            }
-                        }
-                    });
+            if (hideOriginal) {
+                if (isOpening) {
+                    originalView.setVisibility(VISIBLE);
+                    view.finish(dragLayer);
                 } else {
-                    fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
+                    view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
+                    view.mFadeAnimatorSet.start();
                 }
-                fade.start();
-                // TODO: Do not run fade animation until we fix b/129421279.
-                fade.end();
-            };
-        }
+            } else {
+                view.finish(dragLayer);
+            }
+        };
         return view;
     }
 
+    private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) {
+        AnimatorSet fade = new AnimatorSet();
+        fade.setDuration(FADE_DURATION_MS);
+        fade.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                originalView.setVisibility(VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                finish(dragLayer);
+            }
+        });
+
+        if (originalView instanceof FolderIcon) {
+            FolderIcon folderIcon = (FolderIcon) originalView;
+            folderIcon.setBackgroundVisible(false);
+            folderIcon.getFolderName().setTextVisibility(false);
+            fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
+            fade.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    folderIcon.setBackgroundVisible(true);
+                    folderIcon.animateBgShadowAndStroke();
+                    if (folderIcon.hasDot()) {
+                        folderIcon.animateDotScale(0, 1f);
+                    }
+                }
+            });
+        } else {
+            fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
+        }
+
+        return fade;
+    }
+
+    private void finish(DragLayer dragLayer) {
+        ((ViewGroup) dragLayer.getParent()).removeView(this);
+        dragLayer.removeView(mListenerView);
+        recycle();
+    }
+
     private void recycle() {
         setTranslationX(0);
         setTranslationY(0);
@@ -480,10 +515,15 @@
         mBackground = null;
         mClipPath = null;
         mFinalDrawableBounds.setEmpty();
-        mBgDrawableBounds.setEmpty();;
+        mBgDrawableBounds.setEmpty();
         if (mRevealAnimator != null) {
             mRevealAnimator.cancel();
         }
         mRevealAnimator = null;
+        if (mFadeAnimatorSet != null) {
+            mFadeAnimatorSet.cancel();
+        }
+        mFadeAnimatorSet = null;
+        mListenerView.setListener(null);
     }
 }
diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java
new file mode 100644
index 0000000..263f7c4
--- /dev/null
+++ b/src/com/android/launcher3/views/ListenerView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.AbstractFloatingView;
+
+/**
+ * An invisible AbstractFloatingView that can run a callback when it is being closed.
+ */
+public class ListenerView extends AbstractFloatingView {
+
+    public Runnable mCloseListener;
+
+    public ListenerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setVisibility(View.GONE);
+    }
+
+    public void setListener(Runnable listener) {
+        mCloseListener = listener;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mIsOpen = true;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mIsOpen = false;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (mIsOpen) {
+            if (mCloseListener != null) {
+                mCloseListener.run();
+            } else {
+                if (getParent() instanceof ViewGroup) {
+                    ((ViewGroup) getParent()).removeView(this);
+                }
+            }
+        }
+        mIsOpen = false;
+    }
+
+    @Override
+    public void logActionCommand(int command) {
+        // Users do not interact with FloatingIconView, so there is nothing to log here.
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_LISTENER) != 0;
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            handleClose(false);
+        }
+        // We want other views to be able to intercept the touch so we return false here.
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 29866cf..7062369 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -152,9 +152,11 @@
         RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
 
         ArrayList<OptionItem> options = new ArrayList<>();
-        int res = existsStyleWallpapers(launcher) ?
+        int resString = existsStyleWallpapers(launcher) ?
                 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
-        options.add(new OptionItem(res, R.drawable.ic_wallpaper,
+        int resDrawable = existsStyleWallpapers(launcher) ?
+                R.drawable.ic_palette : R.drawable.ic_wallpaper;
+        options.add(new OptionItem(resString, resDrawable,
                 ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
         if (!FeatureFlags.GO_DISABLE_WIDGETS) {
             options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
@@ -212,6 +214,8 @@
                         launcher.getWorkspace().getWallpaperOffsetForCenterPage());
         if (!existsStyleWallpapers(launcher)) {
             intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
+        } else {
+            intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
         }
         String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
         if (!TextUtils.isEmpty(pickerPackage)) {
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 6c0c429..6b6f70d 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -29,6 +29,7 @@
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.WorkspaceItemInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -63,8 +64,8 @@
     }
 
     public static boolean supportsShortcuts(ItemInfo info) {
-        boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo
-                && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi();
+        boolean isItemPromise = info instanceof WorkspaceItemInfo
+                && ((WorkspaceItemInfo) info).hasPromiseIconUi();
         return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                 && !info.isDisabled() && !isItemPromise;
     }
@@ -73,17 +74,13 @@
         return mWasLastCallSuccess;
     }
 
-    public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
-        // mShortcutCache.removeShortcuts(shortcuts);
-    }
-
     /**
      * Queries for the shortcuts with the package name and provided ids.
      *
      * This method is intended to get the full details for shortcuts when they are added or updated,
      * because we only get "key" fields in onShortcutsChanged().
      */
-    public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+    public List<ShortcutInfo> queryForFullDetails(String packageName,
             List<String> shortcutIds, UserHandle user) {
         return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
     }
@@ -92,7 +89,7 @@
      * Gets all the manifest and dynamic shortcuts associated with the given package and user,
      * to be displayed in the shortcuts container on long press.
      */
-    public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
             UserHandle user) {
         return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
                 activity.getPackageName(), activity, null, user);
@@ -148,10 +145,9 @@
         }
     }
 
-    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+    public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
         try {
-            Drawable icon = mLauncherApps.getShortcutIconDrawable(
-                    shortcutInfo.getShortcutInfo(), density);
+            Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
             mWasLastCallSuccess = true;
             return icon;
         } catch (SecurityException|IllegalStateException e) {
@@ -166,22 +162,22 @@
      *
      * If packageName is null, returns all pinned shortcuts regardless of package.
      */
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
         return queryForPinnedShortcuts(packageName, null, user);
     }
 
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
             List<String> shortcutIds, UserHandle user) {
         return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
     }
 
-    public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
+    public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
         return query(FLAG_GET_ALL, null, null, null, user);
     }
 
-    private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
+    private List<String> extractIds(List<ShortcutInfo> shortcuts) {
         List<String> shortcutIds = new ArrayList<>(shortcuts.size());
-        for (ShortcutInfoCompat shortcut : shortcuts) {
+        for (ShortcutInfo shortcut : shortcuts) {
             shortcutIds.add(shortcut.getId());
         }
         return shortcutIds;
@@ -193,7 +189,7 @@
      *
      * TODO: Use the cache to optimize this so we don't make an RPC every time.
      */
-    private List<ShortcutInfoCompat> query(int flags, String packageName,
+    private List<ShortcutInfo> query(int flags, String packageName,
             ComponentName activity, List<String> shortcutIds, UserHandle user) {
         ShortcutQuery q = new ShortcutQuery();
         q.setQueryFlags(flags);
@@ -213,11 +209,7 @@
         if (shortcutInfos == null) {
             return Collections.EMPTY_LIST;
         }
-        List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
-        for (ShortcutInfo shortcutInfo : shortcutInfos) {
-            shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
-        }
-        return shortcutInfoCompats;
+        return shortcutInfos;
     }
 
     public boolean hasHostPermission() {
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 089d672..3686493 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -18,39 +18,39 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3.tests">
 
-    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
 
     <application android:debuggable="true">
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
             android:label="No Config">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                       android:resource="@xml/appwidget_no_config" />
+                       android:resource="@xml/appwidget_no_config"/>
         </receiver>
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
             android:label="Hidden widget">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                android:resource="@xml/appwidget_hidden" />
+                       android:resource="@xml/appwidget_hidden"/>
         </receiver>
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
             android:label="With Config">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                       android:resource="@xml/appwidget_with_config" />
+                       android:resource="@xml/appwidget_with_config"/>
         </receiver>
 
         <activity
@@ -61,8 +61,8 @@
         </activity>
         <activity
             android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
-            android:label="Test Pin Item"
-            android:icon="@drawable/test_drawable_pin_item">
+            android:icon="@drawable/test_drawable_pin_item"
+            android:label="Test Pin Item">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -73,28 +73,28 @@
         <provider
             android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
             android:authorities="${packageName}.commands"
-            android:exported="true" />
+            android:exported="true"/>
 
         <activity
             android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
-            android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
-            android:label="Test launcher"
-            android:stateNotNeeded="true"
-            android:theme="@android:style/Theme.DeviceDefault.Light"
-            android:windowSoftInputMode="adjustPan"
-            android:screenOrientation="unspecified"
             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
-            android:resizeableActivity="true"
-            android:taskAffinity=""
+            android:enabled="false"
+            android:label="Test launcher"
+            android:launchMode="singleTask"
             android:process=":testLauncherProcess"
-            android:enabled="false">
+            android:resizeableActivity="true"
+            android:screenOrientation="unspecified"
+            android:stateNotNeeded="true"
+            android:taskAffinity=""
+            android:theme="@android:style/Theme.DeviceDefault.Light"
+            android:windowSoftInputMode="adjustPan">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.HOME" />
-                <category android:name="android.intent.category.DEFAULT" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.MONKEY"/>
-                <category android:name="android.intent.category.LAUNCHER_APP" />
+                <category android:name="android.intent.category.LAUNCHER_APP"/>
             </intent-filter>
         </activity>
         <activity
@@ -104,6 +104,12 @@
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.launcher3.intent.action.test_shortcut"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts"
+                       android:resource="@xml/shortcuts"/>
         </activity>
     </application>
 </manifest>
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
new file mode 100644
index 0000000..0ad87fb
--- /dev/null
+++ b/tests/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="shortcut1" translatable="false">Shortcut 1</string>
+    <string name="shortcut2" translatable="false">Shortcut 2</string>
+    <string name="shortcut3" translatable="false">Shortcut 3</string>
+</resources>
diff --git a/tests/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml
new file mode 100644
index 0000000..bdc22f9
--- /dev/null
+++ b/tests/res/xml/shortcuts.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+    <shortcut
+        android:shortcutId="shortcut1"
+        android:shortcutShortLabel="@string/shortcut1">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+    <shortcut
+        android:shortcutId="shortcut2"
+        android:shortcutShortLabel="@string/shortcut2">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+    <shortcut
+        android:shortcutId="shortcut3"
+        android:shortcutShortLabel="@string/shortcut3">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+</shortcuts>
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index df935b8..7d60ad6 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -10,11 +10,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.icons.BitmapInfo;
@@ -112,7 +112,7 @@
 
         ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
                 .get(0).getComponentName();
-        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+        WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
                 new Intent().setComponent(cn), false /* allowMissingTarget */, true);
         assertNotNull(info);
         assertTrue(Utilities.isLauncherAppTarget(info.intent));
@@ -124,7 +124,7 @@
         assertTrue(mLoaderCursor.moveToNext());
 
         ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
-        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+        WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
                 new Intent().setComponent(cn), true  /* allowMissingTarget */, true);
         assertNotNull(info);
         assertTrue(Utilities.isLauncherAppTarget(info.intent));
@@ -138,7 +138,7 @@
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
         when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
                 .thenReturn(BitmapInfo.fromBitmap(icon));
-        ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
+        WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
         assertEquals(icon, info.iconBitmap);
         assertEquals("my-shortcut", info.title);
         assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity1.java b/tests/src/com/android/launcher3/testcomponent/MainActivity1.java
new file mode 100644
index 0000000..7ef0ab6
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity1.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity1 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity10.java b/tests/src/com/android/launcher3/testcomponent/MainActivity10.java
new file mode 100644
index 0000000..11df0d2
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity10.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity10 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity2.java b/tests/src/com/android/launcher3/testcomponent/MainActivity2.java
new file mode 100644
index 0000000..dfbba3e
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity2.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity2 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity3.java b/tests/src/com/android/launcher3/testcomponent/MainActivity3.java
new file mode 100644
index 0000000..87d77d9
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity3.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity3 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity4.java b/tests/src/com/android/launcher3/testcomponent/MainActivity4.java
new file mode 100644
index 0000000..dabd2c5
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity4.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity4 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity5.java b/tests/src/com/android/launcher3/testcomponent/MainActivity5.java
new file mode 100644
index 0000000..e58210c
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity5.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity5 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity6.java b/tests/src/com/android/launcher3/testcomponent/MainActivity6.java
new file mode 100644
index 0000000..005d248
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity6.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity6 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity7.java b/tests/src/com/android/launcher3/testcomponent/MainActivity7.java
new file mode 100644
index 0000000..0f21549
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity7.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity7 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity8.java b/tests/src/com/android/launcher3/testcomponent/MainActivity8.java
new file mode 100644
index 0000000..5ba5c55
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity8.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity8 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/MainActivity9.java b/tests/src/com/android/launcher3/testcomponent/MainActivity9.java
new file mode 100644
index 0000000..82b1fcd
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/MainActivity9.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class MainActivity9 extends Activity {
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 9e77937..a37218b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -163,10 +163,12 @@
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
+        // Unlock the phone
+        mDevice.executeShellCommand("input keyevent 82");
     }
 
     @After
-    public void tearDown() {
+    public void verifyLauncherState() {
         try {
             // Limits UI tests affecting tests running after them.
             waitForModelLoaded();
@@ -232,12 +234,8 @@
 
     protected void resetLoaderState() {
         try {
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
-                }
-            });
+            mMainThreadExecutor.execute(
+                    () -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
         } catch (Throwable t) {
             throw new IllegalArgumentException(t);
         }
@@ -278,7 +276,7 @@
     // the results of that gesture because the wait can hide flakeness.
     protected void waitForState(String message, LauncherState state) {
         waitForLauncherCondition(message,
-                launcher -> launcher.getStateManager().getState() == state);
+                launcher -> launcher.getStateManager().getCurrentStableState() == state);
     }
 
     protected void waitForResumed(String message) {
@@ -299,6 +297,19 @@
         Wait.atMost(message, () -> getFromLauncher(condition), timeout);
     }
 
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
+    protected void waitForLauncherCondition(
+            String message,
+            Runnable testThreadAction, Function<Launcher, Boolean> condition,
+            long timeout) {
+        if (!TestHelpers.isInLauncherProcess()) return;
+        Wait.atMost(message, () -> {
+            testThreadAction.run();
+            return getFromLauncher(condition);
+        }, timeout);
+    }
+
     protected LauncherActivityInfo getSettingsApp() {
         return LauncherAppsCompat.getInstance(mTargetContext)
                 .getActivityList("com.android.settings",
@@ -346,7 +357,7 @@
                 mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), LONG_WAIT_TIME_MS));
     }
 
-    protected String resolveSystemApp(String category) {
+    protected static String resolveSystemApp(String category) {
         return getInstrumentation().getContext().getPackageManager().resolveActivity(
                 new Intent(Intent.ACTION_MAIN).addCategory(category),
                 PackageManager.MATCH_SYSTEM_ONLY).
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index 1efdee8..357e029 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -33,6 +33,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,6 +71,7 @@
     }
 
     @Test
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
         writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
 
@@ -84,6 +86,7 @@
     }
 
     @Test
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testCustomProfileLoaded_with_widget() throws Exception {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -103,6 +106,7 @@
     }
 
     @Test
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testCustomProfileLoaded_with_folder() throws Exception {
         writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
                 .addApp(SETTINGS_APP, SETTINGS_APP)
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index e11e62e..c55bc72 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -25,7 +25,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
-import android.content.pm.LauncherActivityInfo;
 import android.util.Log;
 
 import androidx.test.filters.LargeTest;
@@ -62,6 +61,7 @@
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
     private static final String TAG = "TaplTestsAosp";
+    private static final String APP_NAME = "LauncherTestApp";
 
     private static int sScreenshotCount = 0;
 
@@ -119,6 +119,8 @@
         test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
         test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
         test.waitForResumed("Launcher internal state is still Background");
+        // Check that we switched to home.
+        test.mLauncher.getWorkspace();
     }
 
     // Please don't add negative test cases for methods that fail only after a long wait.
@@ -307,12 +309,11 @@
     @PortraitLandscape
     public void testLaunchMenuItem() throws Exception {
         if (!TestHelpers.isInLauncherProcess()) return;
-        final LauncherActivityInfo testApp = getSettingsApp();
 
         final AppIconMenu menu = mLauncher.
                 getWorkspace().
                 switchToAllApps().
-                getAppIcon(testApp.getLabel().toString()).
+                getAppIcon(APP_NAME).
                 openMenu();
 
         executeOnLauncher(
@@ -322,7 +323,7 @@
         final AppIconMenuItem menuItem = menu.getMenuItem(1);
         final String itemName = menuItem.getText();
 
-        menuItem.launch(testApp.getComponentName().getPackageName(), itemName);
+        menuItem.launch(getAppPackageName());
     }
 
     @Test
@@ -330,16 +331,15 @@
     public void testDragAppIcon() throws Throwable {
         try {
             TestProtocol.sDebugTracing = true;
-            final String appName = "LauncherTestApp";
             // 1. Open all apps and wait for load complete.
             // 2. Drag icon to homescreen.
             // 3. Verify that the icon works on homescreen.
             mLauncher.getWorkspace().
                     switchToAllApps().
-                    getAppIcon(appName).
+                    getAppIcon(APP_NAME).
                     dragToWorkspace().
-                    getWorkspaceAppIcon(appName).
-                    launch(getInstrumentation().getContext().getPackageName());
+                    getWorkspaceAppIcon(APP_NAME).
+                    launch(getAppPackageName());
         } finally {
             TestProtocol.sDebugTracing = false;
         }
@@ -349,7 +349,6 @@
     @PortraitLandscape
     public void testDragShortcut() throws Throwable {
         if (!TestHelpers.isInLauncherProcess()) return;
-        LauncherActivityInfo testApp = getSettingsApp();
 
         // 1. Open all apps and wait for load complete.
         // 2. Find the app and long press it to show shortcuts.
@@ -357,7 +356,7 @@
         final AppIconMenuItem menuItem = mLauncher.
                 getWorkspace().
                 switchToAllApps().
-                getAppIcon(testApp.getLabel().toString()).
+                getAppIcon(APP_NAME).
                 openMenu().
                 getMenuItem(0);
         final String shortcutName = menuItem.getText();
@@ -368,6 +367,10 @@
         menuItem.
                 dragToWorkspace().
                 getWorkspaceAppIcon(shortcutName).
-                launch(testApp.getComponentName().getPackageName(), shortcutName);
+                launch(getAppPackageName());
+    }
+
+    public static String getAppPackageName() {
+        return getInstrumentation().getContext().getPackageName();
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 80561fc..84452b4 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -71,25 +71,25 @@
     }
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testWidgetConfig() throws Throwable {
         runTest(false, true);
     }
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testWidgetConfig_rotate() throws Throwable {
         runTest(true, true);
     }
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testConfigCancelled() throws Throwable {
         runTest(false, false);
     }
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testConfigCancelled_rotate() throws Throwable {
         runTest(true, false);
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 7d3cf2b..90c339f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -49,14 +49,14 @@
     @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testDragIcon_portrait() throws Throwable {
         lockRotation(true);
         performTest();
     }
 
     @Test
-    @Ignore
+    @Ignore // Convert test to TAPL and enable them; b/131116002
     public void testDragIcon_landscape() throws Throwable {
         lockRotation(false);
         performTest();
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index c8e7786..af50190 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -103,8 +103,6 @@
         if (mSessionId > -1) {
             mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         }
-
-        super.tearDown();
     }
 
     @Test
@@ -125,7 +123,7 @@
         verifyWidgetPresent(info);
     }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testUnboundWidget_removed() {
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
@@ -145,7 +143,7 @@
         assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
     }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testPendingWidget_autoRestored() {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -183,7 +181,7 @@
                         LauncherSettings.Favorites.APPWIDGET_ID))));
     }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testPendingWidget_notRestored_removed() {
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 95a1289..65d8a82 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -80,7 +80,7 @@
     @Test
     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testPinWidgetNoConfig() throws Throwable {
         runTest("pinWidgetNoConfig", true, new ItemOperator() {
             @Override
@@ -93,7 +93,7 @@
         });
     }
 
-    @Test @Ignore
+        @Test @Ignore // b/131116593
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command =  RequestPinItemActivity.getCommandIntent(
@@ -111,7 +111,7 @@
         }, command);
     }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testPinWidgetWithConfig() throws Throwable {
         runTest("pinWidgetWithConfig", true, new ItemOperator() {
             @Override
@@ -124,7 +124,7 @@
         });
     }
 
-    @Test @Ignore
+    @Test @Ignore // b/131116593
     public void testPinShortcut() throws Throwable {
         // Command to set the shortcut id
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -134,7 +134,7 @@
         runTest("pinShortcut", false, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View view) {
-                return info instanceof ShortcutInfo &&
+                return info instanceof WorkspaceItemInfo &&
                         info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                         ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
             }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 98fb07f..4685c7d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.Direction;
@@ -47,6 +49,7 @@
     private boolean hasClickableIcon(UiObject2 allAppsContainer, BySelector appIconSelector) {
         final UiObject2 icon = allAppsContainer.findObject(appIconSelector);
         if (icon == null) return false;
+        if (mLauncher.getNavigationModel() == ZERO_BUTTON) return true;
         final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
         return icon.getVisibleBounds().bottom < navBar.getVisibleBounds().top;
     }
@@ -63,8 +66,10 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get app icon on all apps")) {
             final UiObject2 allAppsContainer = verifyActiveContainer();
-            final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
-            allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1);
+            if (mLauncher.getNavigationModel() != ZERO_BUTTON) {
+                final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
+                allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1);
+            }
             final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
             if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
                 scrollBackToBeginning();
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index fbeb3a2..44fc3f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -50,4 +50,9 @@
                 downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
         return new AppIconMenu(mLauncher, deepShortcutsContainer);
     }
+
+    @Override
+    protected String getLongPressIndicator() {
+        return "deep_shortcuts_container";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index c39f8d1..ba9c10e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -32,4 +32,9 @@
     public String getText() {
         return mObject.getText();
     }
+
+    @Override
+    protected String getLongPressIndicator() {
+        return "drop_target_bar";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 358d5e9..3b2a7b8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -109,6 +109,6 @@
     }
 
     protected int getSwipeStartY() {
-        return mLauncher.waitForSystemUiObject("navigation_bar_frame").getVisibleBounds().centerY();
+        return mLauncher.getRealDisplaySize().y - 1;
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 16ddba8..1b372ec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -27,7 +27,7 @@
 /**
  * Ancestor for AppIcon and AppMenuItem.
  */
-class Launchable {
+abstract class Launchable {
     protected final LauncherInstrumentation mLauncher;
 
     protected final UiObject2 mObject;
@@ -45,24 +45,17 @@
      * Clicks the object to launch its app.
      */
     public Background launch(String expectedPackageName) {
-        return launch(expectedPackageName, By.pkg(expectedPackageName).depth(0));
+        return launch(By.pkg(expectedPackageName));
     }
 
-    /**
-     * Clicks the object to launch its app.
-     */
-    public Background launch(String expectedPackageName, String expectedAppText) {
-        return launch(expectedPackageName, By.pkg(expectedPackageName).text(expectedAppText));
-    }
-
-    private Background launch(String errorMessage, BySelector selector) {
+    private Background launch(BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter());
         mLauncher.assertTrue(
                 "Launching an app didn't open a new window: " + mObject.getText(),
                 mObject.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
         mLauncher.assertTrue(
-                "App didn't start: " + errorMessage,
+                "App didn't start: " + selector,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
                         LauncherInstrumentation.WAIT_TIME_MS));
         return new Background(mLauncher);
@@ -76,10 +69,13 @@
         Workspace.dragIconToWorkspace(
                 mLauncher,
                 this,
-                new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2));
+                new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2),
+                getLongPressIndicator());
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "dragged launchable to workspace")) {
             return new Workspace(mLauncher);
         }
     }
+
+    protected abstract String getLongPressIndicator();
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3a45e93..27bc43e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -38,6 +38,7 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.ViewConfiguration;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.NonNull;
@@ -154,8 +155,6 @@
         } catch (IOException e) {
             fail(e.toString());
         }
-
-        assertTrue("Phone is locked", !hasSystemUiObject("keyguard_status_view"));
     }
 
     Context getContext() {
@@ -176,6 +175,7 @@
             // Workaround, use constructed context because both the instrumentation context and the
             // app context are not constructed with resources that take overlays into account
             final Context ctx = baseContext.createPackageContext("android", 0);
+            log("Interaction mode = " + getCurrentInteractionMode(ctx));
             if (isGesturalMode(ctx)) {
                 return NavigationModel.ZERO_BUTTON;
             } else if (isSwipeUpMode(ctx)) {
@@ -201,13 +201,19 @@
 
     Closable addContextLayer(String piece) {
         mDiagnosticContext.addLast(piece);
-        return () -> mDiagnosticContext.removeLast();
+        log("Added context: " + getContextDescription());
+        return () -> {
+            log("Removing context: " + getContextDescription());
+            mDiagnosticContext.removeLast();
+        };
     }
 
     private void fail(String message) {
-        final String ctxt = mDiagnosticContext.isEmpty() ? "" : String.join(", ",
-                mDiagnosticContext) + "; ";
-        Assert.fail("http://go/tapl : " + ctxt + message);
+        Assert.fail("http://go/tapl : " + getContextDescription() + message);
+    }
+
+    private String getContextDescription() {
+        return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; ";
     }
 
     void assertTrue(String message, boolean condition) {
@@ -250,12 +256,14 @@
         assertEquals("Unexpected display rotation",
                 mExpectedRotation, mDevice.getDisplayRotation());
         final NavigationModel navigationModel = getNavigationModel();
-        assertTrue("Presence of recents button doesn't match the interaction mode",
-                (navigationModel == NavigationModel.THREE_BUTTON) ==
-                        hasSystemUiObject("recent_apps"));
-        assertTrue("Presence of home button doesn't match the interaction mode",
-                (navigationModel != NavigationModel.ZERO_BUTTON) ==
-                        hasSystemUiObject("home"));
+        final boolean hasRecentsButton = hasSystemUiObject("recent_apps");
+        final boolean hasHomeButton = hasSystemUiObject("home");
+        assertTrue("Presence of recents button doesn't match the interaction mode, mode="
+                        + navigationModel.name() + ", hasRecents=" + hasRecentsButton,
+                (navigationModel == NavigationModel.THREE_BUTTON) == hasRecentsButton);
+        assertTrue("Presence of home button doesn't match the interaction mode, mode="
+                        + navigationModel.name() + ", hasHome=" + hasHomeButton,
+                (navigationModel != NavigationModel.ZERO_BUTTON) == hasHomeButton);
         log("verifyContainerType: " + containerType);
 
         try (Closable c = addContextLayer(
@@ -344,36 +352,17 @@
         final String action;
         if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
             if (hasLauncherObject(WORKSPACE_RES_ID)) {
-                log(action = "0-button: already in workspace");
-            } else if (hasLauncherObject(OVERVIEW_RES_ID)) {
-                log(action = "0-button: from overview");
-                final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
-
-                swipe(
-                        navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                        navBar.getVisibleBounds().centerX(), 0,
-                        NORMAL_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
-            } else if (hasLauncherObject(WIDGETS_RES_ID)) {
-                log(action = "0-button: from widgets");
-                mDevice.pressHome();
-            } else if (hasLauncherObject(APPS_RES_ID)) {
-                log(action = "0-button: from all apps");
-                final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
-
-                swipe(
-                        navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                        navBar.getVisibleBounds().centerX(), 0,
-                        NORMAL_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+                log(action = "already at home");
             } else {
-                log(action = "0-button: from another app");
-                assertTrue("Launcher is visible, don't know how to go home",
-                        !mDevice.hasObject(By.pkg(getLauncherPackageName())));
-                final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
+                log(action = "swiping up to home");
+                final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
+                        ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
+                final Point displaySize = getRealDisplaySize();
 
                 swipe(
-                        navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                        navBar.getVisibleBounds().centerX(), 0,
-                        BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+                        displaySize.x / 2, displaySize.y - 1,
+                        displaySize.x / 2, 0,
+                        finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
             }
         } else {
             log(action = "clicking home button");
@@ -612,11 +601,12 @@
 
     void movePointer(long downTime, long duration, Point from, Point to) {
         final Point point = new Point();
+        final long startTime = SystemClock.uptimeMillis();
         for (; ; ) {
             sleep(16);
 
             final long currentTime = SystemClock.uptimeMillis();
-            final float progress = (currentTime - downTime) / (float) duration;
+            final float progress = (currentTime - startTime) / (float) duration;
             if (progress > 1) return;
 
             point.x = from.x + (int) (progress * (to.x - from.x));
@@ -684,4 +674,10 @@
             return 0;
         }
     }
+
+    Point getRealDisplaySize() {
+        final Point size = new Point();
+        getContext().getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(size);
+        return size;
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index b88da3a..e625510 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -49,11 +49,13 @@
                 "want to switch from overview to all apps")) {
             verifyActiveContainer();
 
-            // Swipe from navbar to the top.
-            final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
+            // Swipe from the prediction row to the top.
             LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
-            final int x = navBar.getVisibleCenter().x;
-            mLauncher.swipe(x, navBar.getVisibleBounds().top - 1, x, 0, ALL_APPS_STATE_ORDINAL);
+            final UiObject2 predictionRow = mLauncher.waitForLauncherObject("prediction_row");
+            mLauncher.swipe(mLauncher.getDevice().getDisplayWidth() / 2,
+                    predictionRow.getVisibleBounds().centerY(),
+                    mLauncher.getDevice().getDisplayWidth() / 2,
+                    0, ALL_APPS_STATE_ORDINAL);
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "swiped all way up from overview")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 943332e..0b3bbd2 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -114,12 +114,16 @@
     public void ensureWorkspaceIsScrollable() {
         final UiObject2 workspace = verifyActiveContainer();
         if (!isWorkspaceScrollable(workspace)) {
-            dragIconToWorkspace(
-                    mLauncher,
-                    getHotseatAppIcon("Chrome"),
-                    new Point(mLauncher.getDevice().getDisplayWidth(),
-                            workspace.getVisibleBounds().centerY()));
-            verifyActiveContainer();
+            try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                    "dragging icon to a second page of workspace to make it scrollable")) {
+                dragIconToWorkspace(
+                        mLauncher,
+                        getHotseatAppIcon("Chrome"),
+                        new Point(mLauncher.getDevice().getDisplayWidth(),
+                                workspace.getVisibleBounds().centerY()),
+                        "deep_shortcuts_container");
+                verifyActiveContainer();
+            }
         }
         assertTrue("Home screen workspace didn't become scrollable",
                 isWorkspaceScrollable(workspace));
@@ -136,17 +140,23 @@
     }
 
     static void dragIconToWorkspace(
-            LauncherInstrumentation launcher, Launchable launchable, Point dest) {
+            LauncherInstrumentation launcher, Launchable launchable, Point dest,
+            String longPressIndicator) {
+        launcher.getTestInfo(TestProtocol.REQUEST_ENABLE_DRAG_LOGGING);
         LauncherInstrumentation.log("dragIconToWorkspace: begin");
         final Point launchableCenter = launchable.getObject().getVisibleCenter();
         final long downTime = SystemClock.uptimeMillis();
         launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
-        launcher.waitForLauncherObject("deep_shortcuts_container");
+        LauncherInstrumentation.log("dragIconToWorkspace: sent down");
+        launcher.waitForLauncherObject(longPressIndicator);
+        LauncherInstrumentation.log("dragIconToWorkspace: indicator");
         launcher.movePointer(downTime, DRAG_DURACTION, launchableCenter, dest);
+        LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
         launcher.sendPointer(
                 downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
         LauncherInstrumentation.log("dragIconToWorkspace: end");
         launcher.waitUntilGone("drop_target_bar");
+        launcher.getTestInfo(TestProtocol.REQUEST_DISABLE_DRAG_LOGGING);
     }
 
     /**