Merge "Use GestureDetector in WorkspaceTouchListener" into ub-launcher3-qt-dev
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/go/quickstep/res/values/dimens.xml b/go/quickstep/res/drawable/default_thumbnail.xml
similarity index 61%
copy from go/quickstep/res/values/dimens.xml
copy to go/quickstep/res/drawable/default_thumbnail.xml
index e2fa387..0a2dbf0 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/drawable/default_thumbnail.xml
@@ -14,13 +14,9 @@
      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
+<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/go/quickstep/res/values/dimens.xml b/go/quickstep/res/drawable/empty_content_box.xml
similarity index 61%
rename from go/quickstep/res/values/dimens.xml
rename to go/quickstep/res/drawable/empty_content_box.xml
index e2fa387..a488388 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/drawable/empty_content_box.xml
@@ -14,13 +14,10 @@
      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
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    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..003ee86
--- /dev/null
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -0,0 +1,33 @@
+<?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_gravity="center_horizontal"
+        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/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..09e2367 100644
--- a/go/quickstep/src/com/android/quickstep/TaskActionController.java
+++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
+
 import android.app.ActivityOptions;
 import android.view.View;
 
@@ -42,7 +44,7 @@
      * @param viewHolder the task view holder to launch
      */
     public void launchTask(TaskHolder viewHolder) {
-        if (viewHolder.getTask() == null) {
+        if (!viewHolder.getTask().isPresent()) {
             return;
         }
         TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
@@ -53,8 +55,9 @@
         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 */);
     }
 
     /**
@@ -63,11 +66,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 +83,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..6f75629 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,103 @@
         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.launchTask(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.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..5755df4 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,12 +15,16 @@
  */
 package com.android.quickstep;
 
-import androidx.annotation.Nullable;
+import android.graphics.Bitmap;
+
+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 +44,28 @@
     }
 
     /**
-     * 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();
+        Bitmap thumbnail = (task.thumbnail != null) ? task.thumbnail.thumbnail : null;
+        if (willAnimate) {
+            mTaskItemView.startContentAnimation(task.icon, thumbnail, task.titleDescription);
+        } else {
+            mTaskItemView.setIcon(task.icon);
+            mTaskItemView.setThumbnail(thumbnail);
+            mTaskItemView.setLabel(task.titleDescription);
+        }
     }
 
     /**
@@ -55,10 +74,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 +82,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..b7ed5b5 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,37 @@
 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.Optional;
 
 /**
  * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -89,24 +101,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.thumbnail);
+                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 +151,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 +186,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 +214,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 +228,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 +263,37 @@
      * the app. In that case, we launch the next most recent.
      */
     public void handleOverviewCommand() {
-        int childCount = mTaskRecyclerView.getChildCount();
-        if (childCount == 0) {
+        // TODO(130735711): Need to address case where most recent task is off screen/unattached.
+        ArrayList<TaskItemView> taskViews = getTaskViews();
+        int taskViewsSize = taskViews.size();
+        if (taskViewsSize <= 1) {
             // Do nothing
             return;
         }
         TaskHolder taskToLaunch;
-        if (mTransitionedFromApp && childCount > 1) {
+        if (mTransitionedFromApp && taskViewsSize > 1) {
             // Launch the next most recent app
-            TaskItemView itemView = (TaskItemView) mTaskRecyclerView.getChildAt(1);
+            TaskItemView itemView = taskViews.get(1);
             taskToLaunch = (TaskHolder) mTaskRecyclerView.getChildViewHolder(itemView);
         } else {
             // Launch the most recent app
-            TaskItemView itemView = (TaskItemView) mTaskRecyclerView.getChildAt(0);
+            TaskItemView itemView = taskViews.get(0);
             taskToLaunch = (TaskHolder) mTaskRecyclerView.getChildViewHolder(itemView);
         }
         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 +302,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 +344,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 +360,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 +379,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 +398,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 +406,7 @@
                 .setDuration(CROSSFADE_DURATION)
                 .setListener(null);
 
+        fadeOutView.animate().cancel();
         fadeOutView.animate()
                 .alpha(0f)
                 .setDuration(CROSSFADE_DURATION)
@@ -338,17 +418,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..7d9916e 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -15,16 +15,21 @@
  */
 package com.android.quickstep.views;
 
+import static com.android.quickstep.views.TaskLayoutUtils.getTaskHeight;
+
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
 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;
@@ -34,16 +39,41 @@
  */
 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 +82,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 +112,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,11 +126,7 @@
         // 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));
     }
 
     /**
@@ -99,16 +135,48 @@
      * @param thumbnail task thumbnail 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);
+        mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnail));
     }
 
     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 Bitmap 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 Bitmap thumbnail) {
+        return (thumbnail != null) ? new BitmapDrawable(getResources(), thumbnail)
+                                   : mDefaultThumbnail;
+    }
+
+    private @NonNull String getSafeLabel(@Nullable String label) {
+        return (label != null) ? label : DEFAULT_LABEL;
+    }
 }
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/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index e865137..be87a96 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -61,6 +61,9 @@
     private static final int STATE_ASSISTANT_ACTIVE = 1;
     private static final int STATE_DELEGATE_ACTIVE = 2;
 
+    private static final String INVOCATION_TYPE_KEY = "invocation_type";
+    private static final int INVOCATION_TYPE_GESTURE = 1;
+
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
     private final PointF mStartDragPos = new PointF();
@@ -238,7 +241,9 @@
                 if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
                     UserEventDispatcher.newInstance(mContext).logActionOnContainer(
                             SWIPE, mDirection, NAVBAR);
-                    mSysUiProxy.startAssistant(new Bundle());
+                    Bundle args = new Bundle();
+                    args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+                    mSysUiProxy.startAssistant(args);
                     mLaunchedAssistant = true;
                 }
             } catch (RemoteException e) {
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 7c71846..3b2d66d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -97,7 +97,7 @@
     public void onAssistantVisibilityChanged(float visibility) {
         Launcher launcher = getCreatedActivity();
         if (launcher != null) {
-            launcher.setQuickSearchBarAlpha(1f - visibility);
+            launcher.onAssistantVisibilityChanged(visibility);
         }
     }
 
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/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/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/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/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index e046cad..42848f4 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -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"));
 
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/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/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 418f1e4..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;
@@ -253,7 +253,7 @@
         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();
         }
 
@@ -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;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0c35566..fff8f02 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -24,6 +24,8 @@
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
@@ -128,6 +130,7 @@
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -147,6 +150,7 @@
 import java.util.List;
 import java.util.function.Predicate;
 
+import androidx.annotation.IdRes;
 import androidx.annotation.Nullable;
 
 /**
@@ -202,6 +206,9 @@
     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
 
+    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
+    private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+
     private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
@@ -223,6 +230,9 @@
     @Thunk AllAppsContainerView mAppsView;
     AllAppsTransitionController mAllAppsController;
 
+    // Scrim view for the all apps and overview state.
+    @Thunk ScrimView mScrimView;
+
     // UI and state for the overview panel
     private View mOverviewPanel;
 
@@ -261,6 +271,8 @@
     final Handler mHandler = new Handler();
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
 
+    private float mCurrentAssistantVisibility = 0f;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
@@ -361,6 +373,24 @@
 
         TraceHelper.endSection("Launcher-onCreate");
         RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
+        mStateManager.addStateListener(new LauncherStateManager.StateListener() {
+            @Override
+            public void onStateTransitionStart(LauncherState toState) {}
+
+            @Override
+            public void onStateTransitionComplete(LauncherState finalState) {
+                float alpha = 1f - mCurrentAssistantVisibility;
+                if (finalState == NORMAL) {
+                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+                } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
+                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+                } else {
+                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+                }
+            }
+        });
     }
 
     @Override
@@ -407,13 +437,6 @@
         onIdpChanged(idp);
     }
 
-    public void setQuickSearchBarAlpha(float alpha) {
-        View qsbAllApps = findViewById(R.id.search_container_all_apps);
-        if (qsbAllApps != null) {
-            qsbAllApps.setAlpha(alpha);
-        }
-    }
-
     private void onIdpChanged(InvariantDeviceProfile idp) {
         mUserEventDispatcher = null;
 
@@ -426,6 +449,18 @@
         rebindModel();
     }
 
+    public void onAssistantVisibilityChanged(float visibility) {
+        mCurrentAssistantVisibility = visibility;
+        float alpha = 1f - visibility;
+        LauncherState state = mStateManager.getState();
+        if (state == NORMAL) {
+            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+        } else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
+            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+            mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+        }
+    }
+
     private void initDeviceProfile(InvariantDeviceProfile idp) {
         // Load configuration-specific DeviceProfile
         mDeviceProfile = idp.getDeviceProfile(this);
@@ -968,6 +1003,9 @@
         // Setup Apps
         mAppsView = findViewById(R.id.apps_view);
 
+        // Setup Scrim
+        mScrimView = findViewById(R.id.scrim_view);
+
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         mDragController.setMoveTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
@@ -1639,7 +1677,7 @@
     public int getCurrentState() {
         if(mStateManager.getState() == LauncherState.ALL_APPS) {
             return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
-        } else if (mStateManager.getState() == LauncherState.OVERVIEW) {
+        } else if (mStateManager.getState() == OVERVIEW) {
             return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
         }
         return StatsLogUtils.LAUNCHER_STATE_HOME;
@@ -1657,7 +1695,7 @@
             LauncherState state = mStateManager.getState();
             if (state == LauncherState.ALL_APPS) {
                 event.srcTarget[2].containerType = ContainerType.ALLAPPS;
-            } else if (state == LauncherState.OVERVIEW) {
+            } else if (state == OVERVIEW) {
                 event.srcTarget[2].containerType = ContainerType.TASKSWITCHER;
             }
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index dfbe0fe..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;
@@ -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,7 +537,7 @@
 
     }
 
-    public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfoCompat info) {
+    public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
         updateAndBindWorkspaceItem(() -> {
             si.updateFromDeepShortcutInfo(info, mApp.getContext());
             LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e738eb7..199220a 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,6 +187,24 @@
         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);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7598450..382eedd 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -33,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;
@@ -67,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;
@@ -625,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) {
@@ -645,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/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index d0e8873..5a2373b 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -19,11 +19,12 @@
 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;
 
 /**
@@ -107,9 +108,9 @@
     }
 
     /**
-     * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfoCompat}.
+     * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
      */
-    public WorkspaceItemInfo(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();
@@ -179,7 +180,7 @@
     /** 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/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 86b96b4..e2a5160 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -49,6 +49,8 @@
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BottomUserEducationView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -70,6 +72,7 @@
     private static final float FLING_VELOCITY_MULTIPLIER = 135f;
     // Starts the springs after at least 55% of the animation has passed.
     private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
+    private static final int ALPHA_CHANNEL_COUNT = 2;
 
     private final Launcher mLauncher;
     private final AdapterHolder[] mAH;
@@ -93,6 +96,8 @@
     private RecyclerViewFastScroller mTouchHandler;
     private final Point mFastScrollerOffset = new Point();
 
+    private final MultiValueAlpha mMultiValueAlpha;
+
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -122,12 +127,18 @@
         addSpringView(R.id.all_apps_header);
         addSpringView(R.id.apps_list_view);
         addSpringView(R.id.all_apps_tabs_view_pager);
+
+        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
     }
 
     public AllAppsStore getAppsStore() {
         return mAllAppsStore;
     }
 
+    public AlphaProperty getAlphaProperty(int index) {
+        return mMultiValueAlpha.getProperty(index);
+    }
+
     @Override
     protected void setDampedScrollShift(float shift) {
         // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4a1d432..a7f89d9 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.SpringObjectAnimator;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 
@@ -64,6 +65,8 @@
         }
     };
 
+    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
+
     private AllAppsContainerView mAppsView;
     private ScrimView mScrimView;
 
@@ -102,7 +105,7 @@
         setScrollRangeDelta(mScrollRangeDelta);
 
         if (mIsVerticalLayout) {
-            mAppsView.setAlpha(1);
+            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1);
             mLauncher.getHotseat().setTranslationY(0);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
         }
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 b64fff4..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;
@@ -34,7 +35,6 @@
 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;
 
@@ -135,14 +135,14 @@
                 });
             }
 
-            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
-            WorkspaceItemInfo info = new WorkspaceItemInfo(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()
-                    .updateAndBindWorkspaceItem(info, compat);
+                    .updateAndBindWorkspaceItem(info, si);
             return info;
         } else {
             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/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/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/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index b0cc87b..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;
@@ -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;
@@ -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/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f8c48ed..0138572 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -30,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;
@@ -66,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;
@@ -285,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);
 
@@ -317,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);
                             }
@@ -473,7 +473,7 @@
 
                                 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.
@@ -839,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/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 671dc54..c37ed99 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,6 +18,7 @@
 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;
@@ -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;
@@ -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);
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 6adcc6e..8528228 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.AllAppsList;
@@ -25,7 +26,6 @@
 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,7 +56,6 @@
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         final Context context = app.getContext();
         DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
-        deepShortcutManager.onShortcutsChanged(mShortcuts);
 
         // Find WorkspaceItemInfo's that have changed on the workspace.
         HashSet<ShortcutKey> removedKeys = new HashSet<>();
@@ -76,9 +75,9 @@
         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<WorkspaceItemInfo> workspaceItemInfos = keyToShortcutInfo.remove(key);
                 if (!fullDetails.isPinned()) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 478800e..2cb256e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -18,6 +18,7 @@
 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;
@@ -28,7 +29,6 @@
 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 {
@@ -82,7 +82,7 @@
                 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) {
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 2b55405..dbfe988 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -17,6 +17,7 @@
 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;
@@ -29,7 +30,6 @@
 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,13 +140,13 @@
                 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 shortcut = shortcuts.get(i);
                 final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
                 // Use unbadged icon for the menu.
                 LauncherIcons li = LauncherIcons.obtain(launcher);
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 199d9c8..9274d44 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.shortcuts;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -43,7 +44,7 @@
     private View mDivider;
 
     private WorkspaceItemInfo mInfo;
-    private ShortcutInfoCompat mDetail;
+    private ShortcutInfo mDetail;
 
     public DeepShortcutView(Context context) {
         this(context, null, 0);
@@ -93,7 +94,7 @@
     }
 
     /** package private **/
-    public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfoCompat detail,
+    public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail,
             PopupContainerWithArrow container) {
         mInfo = info;
         mDetail = detail;
@@ -130,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 5ec1997..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ /dev/null
@@ -1,125 +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;
-import com.android.launcher3.WorkspaceItemInfo;
-
-/**
- * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
- *
- * Not to be confused with {@link WorkspaceItemInfo}.
- */
-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/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index a264f9b..c360117 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -59,6 +59,8 @@
 import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.Themes;
 
 import java.util.List;
@@ -92,6 +94,7 @@
     private static final int WALLPAPERS = R.string.wallpaper_button_text;
     private static final int WIDGETS = R.string.widget_button_text;
     private static final int SETTINGS = R.string.settings_button_text;
+    private static final int ALPHA_CHANNEL_COUNT = 1;
 
     private final Rect mTempRect = new Rect();
     private final int[] mTempPos = new int[2];
@@ -115,6 +118,8 @@
     private final Rect mDragHandleBounds;
     private final RectF mHitRect = new RectF();
 
+    private final MultiValueAlpha mMultiValueAlpha;
+
     private final AccessibilityHelper mAccessibilityHelper;
     @Nullable
     protected Drawable mDragHandle;
@@ -138,6 +143,11 @@
 
         mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
         setFocusable(false);
+        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+    }
+
+    public AlphaProperty getAlphaProperty(int index) {
+        return mMultiValueAlpha.getProperty(index);
     }
 
     @NonNull
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index e7008fd..6b6f70d 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -74,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);
     }
@@ -93,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);
@@ -149,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) {
@@ -167,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;
@@ -194,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);
@@ -214,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/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 71e7880..b351ccf 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -234,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);
         }
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index 33b6f61..357e029 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -71,7 +71,7 @@
     }
 
     @Test
-    @Ignore
+    @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));
 
@@ -86,7 +86,7 @@
     }
 
     @Test
-    @Ignore
+    @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);
@@ -106,7 +106,7 @@
     }
 
     @Test
-    @Ignore
+    @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 dfb0edf..c55bc72 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -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.
@@ -368,7 +370,7 @@
                 launch(getAppPackageName());
     }
 
-    private static String 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..918e6e2 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -125,7 +125,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 +145,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 +183,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 4899334..65d8a82 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -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(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 8f777b6..27bc43e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -175,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)) {