Merge "Revert "Use new onTaskListUpdated() callback"" into ub-launcher3-master
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..f3db20e
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,2 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
index bf76f29..c02fe2c 100644
--- a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
@@ -21,5 +21,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string>
     <string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string>
-    <string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"套用桌布"</string>
 </resources>
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
index 1ccd7d7..66aec40 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
@@ -64,7 +64,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.WORKSPACE;
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index fe159b5..92900f2 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
+import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
 
@@ -151,7 +152,7 @@
     }
 
     @Override
-    public ActivityOptions toActivityOptions(Handler handler, long duration) {
+    public ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
         LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
                 false /* startAtFrontOfQueue */) {
 
@@ -165,7 +166,7 @@
                     );
                     return;
                 }
-                result.setAnimation(createWindowAnimation(targetCompats));
+                result.setAnimation(createWindowAnimation(targetCompats), context);
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
index b82f362..26c3313 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
 
 package com.android.launcher3.model;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 
 import java.lang.ref.WeakReference;
 
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index c20ed12..42b1194 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -25,6 +25,7 @@
 import android.os.UserHandle;
 
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.notification.NotificationKeyData;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,10 +45,6 @@
 
     private DeepShortcutManager() { }
 
-    public static boolean supportsShortcuts(ItemInfo info) {
-        return false;
-    }
-
     /**
      * Queries for the shortcuts with the package name and provided ids.
      *
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
index 311db21..425fb13 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -31,6 +31,9 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
@@ -41,9 +44,6 @@
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.util.Themes;
 
-import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
-
 /**
  * A view which shows a horizontal divider
  */
@@ -288,10 +288,10 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
-            PropertySetter setter, Interpolator fadeInterpolator) {
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
         // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(this, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+        setter.setFloat(this, ALPHA, hasAllAppsContent ? 1 : 0, allAppsFade);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index cb5cbdd..8e064ae 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -32,6 +32,9 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
@@ -62,9 +65,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 @TargetApi(Build.VERSION_CODES.P)
 public class PredictionRowView extends LinearLayout implements
         LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
@@ -80,7 +80,7 @@
 
                 @Override
                 public Integer get(PredictionRowView view) {
-                    return view.mIconCurrentTextAlpha;
+                    return view.mIconLastSetTextAlpha;
                 }
             };
 
@@ -103,6 +103,8 @@
 
     private final int mIconTextColor;
     private final int mIconFullTextAlpha;
+    private int mIconLastSetTextAlpha;
+    // Might use mIconFullTextAlpha instead of mIconLastSetTextAlpha if we are translucent.
     private int mIconCurrentTextAlpha;
 
     private FloatingHeaderView mParent;
@@ -279,7 +281,7 @@
     }
 
     private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
-        if (getAppsStore().getApps().isEmpty()) {
+        if (getAppsStore().getApps().length == 0) {
             // Apps have not been bound yet.
             return Collections.emptyList();
         }
@@ -315,8 +317,14 @@
         }
     }
 
-    public void setTextAlpha(int alpha) {
-        mIconCurrentTextAlpha = alpha;
+    public void setTextAlpha(int textAlpha) {
+        mIconLastSetTextAlpha = textAlpha;
+        if (getAlpha() < 1 && textAlpha > 0) {
+            // If the entire header is translucent, make sure the text is at full opacity so it's
+            // not double-translucent. However, we support keeping the text invisible (alpha == 0).
+            textAlpha = mIconFullTextAlpha;
+        }
+        mIconCurrentTextAlpha = textAlpha;
         int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
         for (int i = 0; i < getChildCount(); i++) {
             ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
@@ -324,6 +332,13 @@
     }
 
     @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+        // Reapply text alpha so that we update it to be full alpha if the row is now translucent.
+        setTextAlpha(mIconLastSetTextAlpha);
+    }
+
+    @Override
     public boolean hasOverlappingRendering() {
         return false;
     }
@@ -351,23 +366,15 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
-            PropertySetter setter, Interpolator fadeInterpolator) {
-        boolean isDrawn = getAlpha() > 0;
-        int textAlpha = hasHeaderExtra
-                ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility
-                : mIconCurrentTextAlpha; // Leave as before
-        if (!isDrawn) {
-            // If the header is not drawn, no need to animate the text alpha
-            setTextAlpha(textAlpha);
-        } else {
-            setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator);
-        }
-
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
+        // Text follows all apps visibility
+        int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0;
+        setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade);
         setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
-                (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR);
+                (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR);
         setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
-                fadeInterpolator);
+                headerFade);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 820a8df..596bc4f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -165,8 +165,7 @@
         }
 
         if (FeatureFlags.PULL_DOWN_STATUS_BAR
-                && !launcher.getDeviceProfile().isMultiWindowMode
-                && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+                && !launcher.getDeviceProfile().isMultiWindowMode) {
             list.add(new StatusBarTouchController(launcher));
         }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 5ee08c1..50cfac8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -19,6 +19,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.util.LayoutUtils;
@@ -68,8 +69,8 @@
         if (taskCount == 0) {
             return super.getOverviewScaleAndTranslation(launcher);
         }
-        TaskView dummyTask = recentsView.getTaskViewAt(Math.max(taskCount - 1,
-                recentsView.getCurrentPage()));
+        TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange(
+                recentsView.getCurrentPage(), 0, taskCount - 1));
         return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
                 .getScaleAndTranslation();
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
index c954762..427206a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -15,7 +15,9 @@
  */
 package com.android.launcher3.uioverrides.states;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 
@@ -43,6 +45,7 @@
         if (this == OVERVIEW_PEEK && fromState == NORMAL) {
             builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
             builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+            builder.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 5543860..151ceb8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -43,7 +43,6 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.SysUINavigationMode;
@@ -128,14 +127,15 @@
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
         } else {
+            boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
+                    && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
             return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
-                    (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
-                            ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
+                    (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
         }
     }
 
     @Override
-    public float getWorkspaceScrimAlpha(Launcher launcher) {
+    public float getOverviewScrimAlpha(Launcher launcher) {
         return 0.5f;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index f06b8a9..ab346c0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -23,13 +23,17 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
 import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
 import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
@@ -43,6 +47,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.util.MotionPauseDetector;
@@ -102,6 +107,9 @@
                 mPeekAnim.start();
                 recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+
+                mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
+                        peekDuration, 0);
             });
         }
     }
@@ -120,6 +128,13 @@
             LauncherState toState) {
         if (fromState == NORMAL && toState == ALL_APPS) {
             AnimatorSetBuilder builder = new AnimatorSetBuilder();
+            // Fade in prediction icons quickly, then rest of all apps after reaching overview.
+            float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
+                    - OVERVIEW.getVerticalProgress(mLauncher);
+            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(ACCEL,
+                    0, ALL_APPS_CONTENT_FADE_THRESHOLD));
+            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(LINEAR,
+                    progressToReachOverview, 1));
 
             // Get workspace out of the way quickly, to prepare for potential pause.
             builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
index 73f328b..9091168 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.WORKSPACE;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 18b8af4..eb571f6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -154,7 +154,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.NAVBAR;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 8e32bb3..00e4f58 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,10 +16,10 @@
 package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -266,8 +266,8 @@
             animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
         }
 
-        float nextFrameProgress = Utilities.boundToRange(
-                progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+        float nextFrameProgress = Utilities.boundToRange(progress
+                + velocity * getSingleFrameMs(mActivity) / Math.abs(mEndDisplacement), 0f, 1f);
 
         mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 5e77e0a..5ebefa3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -81,6 +81,7 @@
                 });
         factory.onRemoteAnimationReceived(null);
         factory.createActivityController(RECENTS_LAUNCH_DURATION);
+        factory.setRecentsAttachedToAppWindow(true, false);
         mActivity = activity;
         mRecentsView = mActivity.getOverviewPanel();
         return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index e3d622f..d627a7f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -411,15 +411,27 @@
         // FolderIconView can be seen morphing into the icon shape.
         final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
         anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
+
+            // Alpha interpolates between [1, 0] between progress values [start, end]
+            final float start = 0f;
+            final float end = 0.85f;
+
+            private float getWindowAlpha(float progress) {
+                if (progress <= start) {
+                    return 1f;
+                }
+                if (progress >= end) {
+                    return 0f;
+                }
+                return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+            }
+
             @Override
             public void onUpdate(RectF currentRect, float progress) {
                 homeAnim.setPlayFraction(progress);
 
-                float alphaProgress = ACCEL_1_5.getInterpolation(progress);
-                float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
-                        windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
                 mTransformParams.setProgress(progress)
-                        .setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
+                        .setCurrentRectAndTargetAlpha(currentRect, getWindowAlpha(progress));
                 if (isFloatingIconView) {
                     mTransformParams.setCornerRadius(endRadius * progress + startRadius
                             * (1f - progress));
@@ -429,7 +441,8 @@
 
                 if (isFloatingIconView) {
                     ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
-                            windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
+                            windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(),
+                            false);
                 }
             }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index c43155b..8c5a788 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -80,7 +80,9 @@
 
     @Override
     public void onAssistantVisibilityChanged(float visibility) {
-        // TODO:
+        // This class becomes active when the screen is locked.
+        // Rather than having it handle assistant visibility changes, the assistant visibility is
+        // set to zero prior to this class becoming active.
     }
 
     @NonNull
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 6533c63..a94f25d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -99,6 +99,7 @@
 
         @Override
         protected void onTransitionComplete() {
+            // TODO(b/138729100) This doesn't execute first time launcher is run
             if (mTriggeredFromAltTab) {
                 RecentsView rv = (RecentsView) mHelper.getVisibleRecentsView();
                 if (rv == null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 20330b2..9bdc98b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -161,7 +161,7 @@
                         mFallbackRecentsView.resetViewUI();
                     }
                 });
-                result.setAnimation(anim);
+                result.setAnimation(anim, RecentsActivity.this);
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
index ca89c33..e51ba63 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -188,9 +188,9 @@
         return true;
     }
 
-    public void setCancelWithDeferredScreenshot(boolean deferredWithScreenshot) {
+    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
         if (targetSet != null) {
-            targetSet.controller.setCancelWithDeferredScreenshot(deferredWithScreenshot);
+            targetSet.controller.setDeferCancelUntilNextTransition(defer, screenshot);
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
index cfd14bb..1af0db0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -205,8 +205,7 @@
                         }
                     };
                     WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
-                            future, animStartedListener, mHandler, true /* scaleUp */,
-                            v.getDisplay().getDisplayId());
+                            future, animStartedListener, mHandler, true /* scaleUp */, displayId);
                 }
             });
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 6f36b05..86ba855 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
@@ -68,6 +69,7 @@
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
@@ -145,6 +147,11 @@
 
     private static final String TAG = "TouchInteractionService";
 
+    private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
+    private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
+    private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
+    private int mBackGestureNotificationCounter = -1;
+
     private final IBinder mMyBinder = new IOverviewProxy.Stub() {
 
         public void onActiveNavBarRegionChanges(Region region) {
@@ -205,6 +212,10 @@
                     mOverviewComponentObserver.getActivityControlHelper();
             UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
                     isButton, gestureSwipeLeft, activityControl.getContainerType());
+
+            if (completed && !isButton && shouldNotifyBackGesture()) {
+                BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
+            }
         }
 
         public void onSystemUiStateChanged(int stateFlags) {
@@ -335,6 +346,9 @@
         if (mInputEventReceiver != null) {
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "disposeEventHandlers");
+            }
         }
         if (mInputMonitorCompat != null) {
             mInputMonitorCompat.dispose();
@@ -476,6 +490,8 @@
 
         // Temporarily disable model preload
         // new ModelPreload().start(this);
+        mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
+                .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
 
         Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
     }
@@ -566,6 +582,7 @@
     private boolean validSystemUiFlags() {
         return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
                 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
                 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
                         || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
     }
@@ -640,10 +657,12 @@
             // In the case where we are in the excluded assistant state, ignore it and treat the
             // running activity as the task behind the assistant
             runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
-            final ComponentName homeComponent =
+            if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
+                final ComponentName homeComponent =
                     mOverviewComponentObserver.getHomeIntent().getComponent();
-            forceOverviewInputConsumer =
+                forceOverviewInputConsumer =
                     runningTaskInfo.baseIntent.getComponent().equals(homeComponent);
+            }
         }
 
         if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
@@ -723,7 +742,7 @@
             return new OverviewInputConsumer(activity, mInputMonitorCompat,
                     false /* startingInActivityBounds */);
         } else {
-            return new OverviewWithoutFocusInputConsumer(this, mInputMonitorCompat,
+            return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat,
                     disableHorizontalSwipe(event));
         }
     }
@@ -816,8 +835,9 @@
             pw.println("  assistantAvailable=" + mAssistantAvailable);
             pw.println("  assistantDisabled="
                     + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
-            pw.println("  resumed="
-                    + mOverviewComponentObserver.getActivityControlHelper().isResumed());
+            boolean resumed = mOverviewComponentObserver != null
+                    && mOverviewComponentObserver.getActivityControlHelper().isResumed();
+            pw.println("  resumed=" + resumed);
             pw.println("  useSharedState=" + mConsumer.useSharedSwipeState());
             if (mConsumer.useSharedSwipeState()) {
                 sSwipeSharedState.dump("    ", pw);
@@ -860,6 +880,22 @@
                 mRecentsModel, mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
     }
 
+    protected boolean shouldNotifyBackGesture() {
+        return mBackGestureNotificationCounter > 0 &&
+                mGestureBlockingActivity != null;
+    }
+
+    @WorkerThread
+    protected void tryNotifyBackGesture() {
+        if (shouldNotifyBackGesture()) {
+            mBackGestureNotificationCounter--;
+            Utilities.getDevicePrefs(this).edit()
+                    .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
+            sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
+                    mGestureBlockingActivity.getPackageName()));
+        }
+    }
+
     public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) {
         BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
                 .startRecentsActivity(intent, null, listener, null, null));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index cc9719b..7a67a2a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,12 +17,12 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
@@ -780,14 +780,14 @@
             interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
         } else {
             startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
-                    * SINGLE_FRAME_MS / mTransitionDragLength, 0, mDragLengthFactor);
+                    * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
             float minFlingVelocity = mContext.getResources()
                     .getDimension(R.dimen.quickstep_fling_min_velocity);
             if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
                 if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
                     Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
                             startShift, endShift, endShift, endVelocity / 1000,
-                            mTransitionDragLength);
+                            mTransitionDragLength, mContext);
                     endShift = overshoot.end;
                     interpolator = overshoot.interpolator;
                     duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION,
@@ -943,17 +943,16 @@
         }
         if (start == end || duration <= 0) {
             mLauncherTransitionController.dispatchSetInterpolator(t -> end);
-            mLauncherTransitionController.getAnimationPlayer().end();
         } else {
             mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
             mAnimationFactory.adjustActivityControllerInterpolators();
-            mLauncherTransitionController.getAnimationPlayer().setDuration(duration);
-
-            if (QUICKSTEP_SPRINGS.get()) {
-                mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
-            }
-            mLauncherTransitionController.getAnimationPlayer().start();
         }
+        mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
+
+        if (QUICKSTEP_SPRINGS.get()) {
+            mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
+        }
+        mLauncherTransitionController.getAnimationPlayer().start();
         mHasLauncherTransitionControllerStarted = true;
     }
 
@@ -1186,7 +1185,8 @@
     private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
         endLauncherTransitionController();
         mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
-        mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
+        mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+                true /* screenshot */);
         mRecentsView.onSwipeUpAnimationSuccess();
 
         RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 631c34c..6ec1da0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -50,6 +50,7 @@
 import com.android.quickstep.util.ObjectWrapper;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -335,7 +336,8 @@
 
                 ThumbnailData thumbnail =
                         mRecentsAnimationWrapper.targetSet.controller.screenshotTask(mRunningTaskId);
-                mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
+                mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+                        false /* screenshot */);
 
                 ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
                 ActivityOptionsCompat.setFreezeRecentTasksList(options);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 425b8b6..05cbb78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -30,7 +30,13 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -131,12 +137,14 @@
                 ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
 
         final boolean triggerQuickstep;
+        int touch = Touch.FLING;
         if (Math.abs(velocity) >= ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) {
             triggerQuickstep = velocity > 0;
         } else {
             float displacementX = mDisableHorizontalSwipe ? 0 : (ev.getX() - mDownPos.x);
             float displacementY = ev.getY() - mDownPos.y;
             triggerQuickstep = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
+            touch = Touch.SWIPE;
         }
 
         if (triggerQuickstep) {
@@ -144,6 +152,13 @@
             ActivityManagerWrapper.getInstance()
                     .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
             TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+            BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
+            int pageIndex = -1; // This number doesn't reflect workspace page index.
+                                // It only indicates that launcher client screen was shown.
+            int containerType = StatsLogUtils.getContainerTypeFromState(activity.getCurrentState());
+            activity.getUserEventDispatcher().logActionOnContainer(
+                    touch, Direction.UP, containerType, pageIndex);
+            activity.getUserEventDispatcher().setPreviousHomeGesture(true);
         } else {
             // ignore
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
index 83601e6..14083dd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RecentsAnimationListener;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -39,7 +40,7 @@
 public class RecentsAnimationListenerSet implements RecentsAnimationListener {
 
     // The actual app surface is replaced by a screenshot upon recents animation cancelation when
-    // deferredWithScreenshot is true. Launcher takes the responsibility to clean up this screenshot
+    // the thumbnailData exists. Launcher takes the responsibility to clean up this screenshot
     // after app transition is finished. This delay is introduced to cover the app transition
     // period of time.
     private final int TRANSITION_DELAY = 100;
@@ -90,14 +91,14 @@
     }
 
     @Override
-    public final void onAnimationCanceled(boolean deferredWithScreenshot) {
+    public final void onAnimationCanceled(ThumbnailData thumbnailData) {
         Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
             for (SwipeAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationCanceled();
             }
         });
         // TODO: handle the transition better instead of simply using a transition delay.
-        if (deferredWithScreenshot) {
+        if (thumbnailData != null) {
             MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
                     TRANSITION_DELAY);
         }
@@ -109,6 +110,6 @@
 
     public void cancelListener() {
         mCancelled = true;
-        onAnimationCanceled(false);
+        onAnimationCanceled(null);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 1705c97..1069bed 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -15,6 +15,11 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -35,15 +40,11 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.graphics.OverviewScrim;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
 /**
  * Creates an animation where all the workspace items are moved into their final location,
  * staggered row by row from the bottom up.
@@ -122,8 +123,8 @@
             addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
         }
 
-        addWorkspaceScrimAnimationForState(launcher, BACKGROUND_APP, 0);
-        addWorkspaceScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+        addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
+        addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
 
         AnimatorListener resetClipListener = new AnimatorListenerAdapter() {
             int numAnimations = mAnimators.size();
@@ -191,13 +192,17 @@
         mAnimators.add(alpha);
     }
 
-    private void addWorkspaceScrimAnimationForState(Launcher launcher, LauncherState state,
-            long duration) {
+    private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
         AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder();
         AnimationConfig scrimAnimConfig = new AnimationConfig();
         scrimAnimConfig.duration = duration;
         PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
         launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
         mAnimators.add(scrimAnimBuilder.build());
+        Animator fadeOverviewScrim = ObjectAnimator.ofFloat(
+                launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
+                state.getOverviewScrimAlpha(launcher));
+        fadeOverviewScrim.setDuration(duration);
+        mAnimators.add(fadeOverviewScrim);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 78f6ffa..a8e2956 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.systemui.shared.recents.utilities.Utilities
         .postAtFrontOfQueueAsynchronously;
 
@@ -24,6 +24,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
+import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
 
@@ -66,7 +67,7 @@
 
     /**
      * Called on the UI thread when the animation targets are received. The implementation must
-     * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
+     * call {@link AnimationResult#setAnimation} with the target animation to be run.
      */
     @UiThread
     public abstract void onCreateAnimation(
@@ -110,7 +111,7 @@
         }
 
         @UiThread
-        public void setAnimation(AnimatorSet animation) {
+        public void setAnimation(AnimatorSet animation, Context context) {
             if (mInitialized) {
                 throw new IllegalStateException("Animation already initialized");
             }
@@ -134,7 +135,7 @@
 
                 // Because t=0 has the app icon in its original spot, we can skip the
                 // first frame and have the same movement one frame earlier.
-                mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS);
+                mAnimator.setCurrentPlayTime(getSingleFrameMs(context));
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index c5c5323..b9ce1ce 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -85,7 +85,7 @@
 
         register();
 
-        Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+        Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
         context.startActivity(addToIntent(new Intent((intent))), options);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 44324cb..991408c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
+import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -218,7 +219,7 @@
                         anim.addListener(mForceInvisibleListener);
                     }
 
-                    result.setAnimation(anim);
+                    result.setAnimation(anim, mLauncher);
                 }
             };
 
@@ -495,6 +496,8 @@
             endCrop = windowTargetBounds.height();
         }
 
+        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
+                ? startCrop / 2f : 0f;
         final float windowRadius = mDeviceProfile.isMultiWindowMode
                 ? 0 : getWindowCornerRadius(mLauncher.getResources());
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
@@ -506,7 +509,7 @@
                     alphaDuration, LINEAR);
             FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
                     EXAGGERATED_EASE);
-            FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0,
+            FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
                     RADIUS_DURATION, EXAGGERATED_EASE);
 
             @Override
@@ -819,7 +822,7 @@
             }
 
             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
-            result.setAnimation(anim);
+            result.setAnimation(anim, mLauncher);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index f0204b9..174e49b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -19,16 +19,20 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 
 import android.util.FloatProperty;
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
@@ -36,8 +40,7 @@
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
-
-import androidx.annotation.NonNull;
+import com.android.launcher3.graphics.OverviewScrim;
 
 /**
  * State handler for recents view. Manages UI changes and animations for recents view based off the
@@ -67,6 +70,8 @@
         mRecentsView.setTranslationX(translationX);
         mRecentsView.setTranslationY(scaleAndTranslation.translationY);
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+        OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
+        SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
     }
 
     @Override
@@ -110,6 +115,9 @@
                 translateYInterpolator);
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+        OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
+        setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
+                builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 97cd38a..c02df93 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -32,9 +32,11 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.Activity;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.util.Base64;
@@ -244,4 +246,9 @@
         }
         return new ScaleAndTranslation(1.1f, 0f, 0f);
     }
+
+    public static Person[] getPersons(ShortcutInfo si) {
+        Person[] persons = si.getPersons();
+        return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 0605953..bb72315 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return LauncherLogProto.ContainerType.NAVBAR;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 109d751..b81edfa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -62,7 +62,7 @@
     /**
      * The progress at which all apps content will be fully visible when swiping up from overview.
      */
-    private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
+    protected static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
 
     /**
      * The progress at which recents will begin fading out when swiping up from overview.
@@ -147,8 +147,8 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
-        return ContainerType.HOTSEAT;
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
+        return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
     }
 
     private AnimatorSetBuilder getNormalToOverviewAnimation() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index f5ba372..11a8043 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -33,6 +33,9 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsModel;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -132,8 +135,12 @@
 
     @Override
     public final boolean onControllerTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == ACTION_UP || ev.getAction() == ACTION_CANCEL) {
+        int action = ev.getAction();
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
             dispatchTouchEvent(ev);
+            mLauncher.getUserEventDispatcher().logActionOnContainer(action == ACTION_UP ?
+                    Touch.FLING : Touch.SWIPE, Direction.DOWN, ContainerType.WORKSPACE,
+                    mLauncher.getWorkspace().getCurrentPage());
             setWindowSlippery(false);
             return true;
         }
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 4a2ed3a..88a4eb6 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -116,6 +116,14 @@
                 .getHomeActivities(new ArrayList<>());
 
         mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
+
+        // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
+        // launcher made invisible become visible again before the new activity control helper
+        // becomes active.
+        if (mActivityControlHelper != null) {
+            mActivityControlHelper.onAssistantVisibilityChanged(0.f);
+        }
+
         if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
                 && (defaultHome == null || mIsDefaultHome)) {
             // User default home is same as out home app. Use Overview integrated in Launcher.
@@ -131,6 +139,7 @@
             }
         } else {
             // The default home app is a different launcher. Use the fallback Overview instead.
+
             mActivityControlHelper = new FallbackActivityControllerHelper();
             mIsHomeAndOverviewSame = false;
             mOverviewIntent = mFallbackIntent;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
index 0822e61..f9d2f11 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -68,7 +68,7 @@
             Context context, Handler handler, long duration) {
         register();
 
-        Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+        Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
         context.startActivity(intent, options);
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index a7e6d74..4503a43 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -17,6 +17,7 @@
 
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
+import android.content.Context;
 import android.os.Handler;
 
 import com.android.launcher3.LauncherAnimationRunner;
@@ -32,14 +33,14 @@
 
     AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
 
-    default ActivityOptions toActivityOptions(Handler handler, long duration) {
+    default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
         LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
                 false /* startAtFrontOfQueue */) {
 
             @Override
             public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
                     AnimationResult result) {
-                result.setAnimation(createWindowAnimation(targetCompats));
+                result.setAnimation(createWindowAnimation(targetCompats), context);
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 63c8023..3747f9a 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -74,6 +74,9 @@
     private int mMidAlpha;
     private float mMidProgress;
 
+    // The progress at which the drag handle starts moving up with the shelf.
+    private float mDragHandleProgress;
+
     private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
     private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
 
@@ -95,7 +98,7 @@
 
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
+        mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255);
 
         mEndAlpha = Color.alpha(mEndScrim);
         mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
@@ -150,15 +153,16 @@
 
             if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
                 mMidProgress = 1;
+                mDragHandleProgress = 1;
                 mMidAlpha = 0;
             } else {
                 mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+                mMidProgress =  OVERVIEW.getVerticalProgress(mLauncher);
                 Rect hotseatPadding = dp.getHotseatLayoutPadding();
                 int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
                         - hotseatPadding.bottom - hotseatPadding.top;
-                float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
-                mMidProgress =  1 - (arrowTop / mShiftRange);
-
+                float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
+                mDragHandleProgress =  1 - (dragHandleTop / mShiftRange);
             }
             mTopOffset = dp.getInsets().top - mShelfOffset;
             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
@@ -199,8 +203,6 @@
                     mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
             mShelfColor = setColorAlphaBound(mEndScrim, alpha);
         } else {
-            mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
-
             // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
             int alpha = Math.round(
                     Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
@@ -212,6 +214,10 @@
                             (float) 0, LINEAR));
             mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
         }
+
+        if (mProgress < mDragHandleProgress) {
+            mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress);
+        }
     }
 
     @Override
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index b6cd1be..f27f400 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -27,6 +27,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.util.Log;
 
 import androidx.test.uiautomator.UiDevice;
@@ -102,36 +103,55 @@
                         if (mode == THREE_BUTTON || mode == ALL) {
                             evaluateWithThreeButtons();
                         }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception", e);
+                        throw e;
                     } finally {
-                        setActiveOverlay(prevOverlayPkg, originalMode);
+                        Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
                     }
                     mLauncher.disableDebugTracing();
                 }
 
-                public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
-                    base.evaluate();
-                }
-
                 private void evaluateWithThreeButtons() throws Throwable {
-                    setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.THREE_BUTTON);
-                    evaluateWithoutChangingSetting(base);
+                    if (setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.THREE_BUTTON)) {
+                        base.evaluate();
+                    }
                 }
 
                 private void evaluateWithTwoButtons() throws Throwable {
-                    setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.TWO_BUTTON);
-                    base.evaluate();
+                    if (setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.TWO_BUTTON)) {
+                        base.evaluate();
+                    }
                 }
 
                 private void evaluateWithZeroButtons() throws Throwable {
-                    setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
-                            LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
-                    base.evaluate();
+                    if (setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.ZERO_BUTTON)) {
+                        base.evaluate();
+                    }
                 }
 
-                private void setActiveOverlay(String overlayPackage,
+                private boolean packageExists(String packageName) {
+                    try {
+                        PackageManager pm = getInstrumentation().getContext().getPackageManager();
+                        if (pm.getApplicationInfo(packageName, 0 /* flags */) == null) {
+                            return false;
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        return false;
+                    }
+                    return true;
+                }
+
+                private boolean setActiveOverlay(String overlayPackage,
                         LauncherInstrumentation.NavigationModel expectedMode) throws Exception {
+                    if (!packageExists(overlayPackage)) {
+                        Log.d(TAG, "setActiveOverlay: " + overlayPackage + " pkg does not exist");
+                        return false;
+                    }
+
                     setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
                             overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
                     setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
@@ -175,6 +195,7 @@
                     Assert.assertTrue("Switching nav mode: " + error, error == null);
 
                     Thread.sleep(5000);
+                    return true;
                 }
 
                 private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b796140e..3b0432e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -88,7 +88,7 @@
     <string name="notification_dots_title" msgid="9062440428204120317">"नई सूचनाएं बताने वाला गोल निशान"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"चालू"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"चालू"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचना के एक्सेस की ज़रूरत है"</string>
+    <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचना के ऐक्सेस की ज़रूरत है"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"सूचना बिंदु दिखाने के लिए, <xliff:g id="NAME">%1$s</xliff:g> के ऐप्लिकेशन सूचना चालू करें"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"सेटिंग बदलें"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"नई सूचनाएं बताने वाला गोल निशान दिखाएं"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 672665e..c926bc1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -89,7 +89,7 @@
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"ஆன்"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"ஆஃப்"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"அறிவிப்பிற்கான அணுகல் தேவை"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"அறிவிப்புப் புள்ளிகளைக் காட்ட, <xliff:g id="NAME">%1$s</xliff:g> இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"அறிவிப்புப் புள்ளிகளைக் காட்ட, <xliff:g id="NAME">%1$s</xliff:g> இன் ஆப்ஸ் அறிவிப்புகளை இயக்கவும்"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"அமைப்புகளை மாற்று"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"அறிவிப்புப் புள்ளிகளைக் காட்டு"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 881f65d..339aef5 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -104,6 +104,7 @@
     </style>
 
     <style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
+        <item name="android:colorControlHighlight">#75212121</item>
         <item name="allAppsInterimScrimAlpha">25</item>
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderTextColor">?attr/workspaceTextColor</item>
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index ab39274..bc936b7 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -15,14 +15,13 @@
 import android.os.Process;
 import android.os.UserHandle;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppFilter;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.icons.IconCache;
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index d884049..2910525 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -29,11 +29,19 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
+import java.util.Comparator;
+
 /**
  * Represents an app in AllAppsView.
  */
 public class AppInfo extends ItemInfoWithIcon {
 
+    public static AppInfo[] EMPTY_ARRAY = new AppInfo[0];
+    public static Comparator<AppInfo> COMPONENT_KEY_COMPARATOR = (a, b) -> {
+        int uc = a.user.hashCode() - b.user.hashCode();
+        return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
+    };
+
     /**
      * The intent used to start the application.
      */
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index d949141..e3ef5d6 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -11,6 +11,7 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.provider.RestoreDbTask;
@@ -18,6 +19,8 @@
 
 import androidx.annotation.WorkerThread;
 
+import static android.os.Process.myUserHandle;
+
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
 
     private static final String TAG = "AWRestoredReceiver";
@@ -77,9 +80,14 @@
                 state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
             }
 
-            String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+            // b/135926478: Work profile widget restore is broken in platform. This forces us to
+            // recreate the widget during loading with the correct host provider.
+            long mainProfileId = UserManagerCompat.getInstance(context)
+                    .getSerialNumberForUser(myUserHandle());
+            String oldWidgetId = Integer.toString(oldWidgetIds[i]);
             int result = new ContentWriter(context, new ContentWriter.CommitParams(
-                    "appWidgetId=? and (restored & 1) = 1", widgetIdParams))
+                    "appWidgetId=? and (restored & 1) = 1 and profileId=?",
+                    new String[] { oldWidgetId, Long.toString(mainProfileId) }))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
@@ -87,7 +95,7 @@
             if (result == 0) {
                 Cursor cursor = cr.query(Favorites.CONTENT_URI,
                         new String[] {Favorites.APPWIDGET_ID},
-                        "appWidgetId=?", widgetIdParams, null);
+                        "appWidgetId=?", new String[] { oldWidgetId }, null);
                 try {
                     if (!cursor.moveToFirst()) {
                         // The widget no long exists.
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 8de0069..f61051f 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -174,7 +174,7 @@
             getUserEventDispatcher().logAppLaunch(v, intent);
             getStatsLogManager().logAppLaunch(v, intent);
             return true;
-        } catch (ActivityNotFoundException|SecurityException e) {
+        } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
         }
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index c967a96..6c5bc40 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -108,17 +108,20 @@
                     // For the second frame, if the first frame took more than 16ms,
                     // adjust the start time and pretend it took only 16ms anyway. This
                     // prevents a large jump in the animation due to an expensive first frame
-                } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
-                        !mAdjustedSecondFrameTime &&
-                        currentTime > mStartTime + SINGLE_FRAME_MS &&
-                        currentPlayTime > SINGLE_FRAME_MS) {
-                    animation.setCurrentPlayTime(SINGLE_FRAME_MS);
-                    mAdjustedSecondFrameTime = true;
                 } else {
-                    if (frameNum > 1) {
-                        mRootView.post(() -> animation.removeUpdateListener(this));
+                    int singleFrameMS = getSingleFrameMs(mRootView.getContext());
+                    if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+                            !mAdjustedSecondFrameTime &&
+                            currentTime > mStartTime + singleFrameMS &&
+                            currentPlayTime > singleFrameMS) {
+                        animation.setCurrentPlayTime(singleFrameMS);
+                        mAdjustedSecondFrameTime = true;
+                    } else {
+                        if (frameNum > 1) {
+                            mRootView.post(() -> animation.removeUpdateListener(this));
+                        }
+                        if (DEBUG) print(animation);
                     }
-                    if (DEBUG) print(animation);
                 }
                 mHandlingOnAnimationUpdate = false;
             } else {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 40d7668..c262e16 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -105,6 +105,7 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
 import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
@@ -112,7 +113,6 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.states.InternalStateHandler;
 import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -129,6 +129,7 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.RaceConditionTracker;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
@@ -163,7 +164,7 @@
  * Default launcher application.
  */
 public class Launcher extends BaseDraggingActivity implements LauncherExterns,
-        LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate,
+        Callbacks, LauncherProviderChangeListener, UserEventDelegate,
         InvariantDeviceProfile.OnIDPChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
@@ -875,9 +876,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
-
-        getUserEventDispatcher().logActionCommand(Action.Command.STOP,
-                mStateManager.getState().containerType, -1);
+        logStopAndResume(Action.Command.STOP);
 
         mAppWidgetHost.setListenIfResumed(false);
 
@@ -903,8 +902,7 @@
 
     private void handleDeferredResume() {
         if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
-            getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
-                    mStateManager.getState().containerType, -1);
+            logStopAndResume(Action.Command.RESUME);
             getUserEventDispatcher().startSession();
 
             UiFactory.onLauncherStateOrResumeChanged(this);
@@ -931,6 +929,17 @@
         }
     }
 
+    private void logStopAndResume(int command) {
+        int containerType = mStateManager.getState().containerType;
+        if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
+            getUserEventDispatcher().logActionCommand(command,
+                containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
+        } else {
+            getUserEventDispatcher().logActionCommand(command, containerType, -1);
+        }
+
+    }
+
     protected void onStateSet(LauncherState state) {
         getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
         if (mDeferredResumePending) {
@@ -1899,6 +1908,10 @@
         if (mPendingExecutor != null) {
             mPendingExecutor.markCompleted();
             mPendingExecutor = null;
+
+            // We might have set this flag previously and forgot to clear it.
+            mAppsView.getAppsStore()
+                    .disableDeferUpdatesSilently(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
         }
     }
 
@@ -2251,9 +2264,7 @@
 
     @Override
     public void executeOnNextDraw(ViewOnDrawExecutor executor) {
-        if (mPendingExecutor != null) {
-            mPendingExecutor.markCompleted();
-        }
+        clearPendingBinds();
         mPendingExecutor = executor;
         if (!isInState(ALL_APPS)) {
             mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
@@ -2335,7 +2346,7 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAllApplications(ArrayList<AppInfo> apps) {
+    public void bindAllApplications(AppInfo[] apps) {
         mAppsView.getAppsStore().setApps(apps);
     }
 
@@ -2348,16 +2359,6 @@
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
     }
 
-    /**
-     * A package was updated.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
-     */
-    @Override
-    public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
-        mAppsView.getAppsStore().addOrUpdateApps(apps);
-    }
-
     @Override
     public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
         mAppsView.getAppsStore().updatePromiseAppProgress(app);
@@ -2405,11 +2406,6 @@
     }
 
     @Override
-    public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
-        mAppsView.getAppsStore().removeApps(appInfos);
-    }
-
-    @Override
     public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
         mPopupDataProvider.setAllWidgets(allWidgets);
     }
@@ -2494,7 +2490,7 @@
                         KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
             }
             if (currentFocus.getTag() instanceof ItemInfo
-                    && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
+                    && ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
                 shortcutInfos.add(new KeyboardShortcutInfo(
                         getString(R.string.shortcuts_menu_with_notifications_description),
                         KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d79f5d5..eed2377 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -38,8 +38,10 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
+import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.CacheDataUpdatedTask;
 import com.android.launcher3.model.LoaderResults;
 import com.android.launcher3.model.LoaderTask;
@@ -49,20 +51,14 @@
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.CancellationException;
@@ -133,33 +129,6 @@
         }
     };
 
-    public interface Callbacks {
-        public void rebindModel();
-
-        public int getCurrentWorkspaceScreen();
-        public void clearPendingBinds();
-        public void startBinding();
-        public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
-        public void bindScreens(IntArray orderedScreenIds);
-        public void finishFirstPageBind(ViewOnDrawExecutor executor);
-        public void finishBindingItems(int pageBoundFirst);
-        public void bindAllApplications(ArrayList<AppInfo> apps);
-        public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps);
-        public void preAddApps();
-        public void bindAppsAdded(IntArray newScreens,
-                ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
-        public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
-        public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
-        public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
-        public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
-        public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
-        public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
-        public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
-        public void onPageBoundSynchronously(int page);
-        public void executeOnNextDraw(ViewOnDrawExecutor executor);
-        public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
-    }
-
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
@@ -411,16 +380,7 @@
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
                 apps.addPromiseApp(app.getContext(), sessionInfo);
-                if (!apps.added.isEmpty()) {
-                    final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
-                    apps.added.clear();
-                    scheduleCallbackTask(new CallbackTask() {
-                        @Override
-                        public void execute(Callbacks callbacks) {
-                            callbacks.bindAppsAddedOrUpdated(arrayList);
-                        }
-                    });
-                }
+                bindApplicationsIfNeeded();
             }
         });
     }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index dcfd272..6e2626b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -243,6 +243,10 @@
         return 0;
     }
 
+    public float getOverviewScrimAlpha(Launcher launcher) {
+        return 0;
+    }
+
     public String getDescription(Launcher launcher) {
         return launcher.getWorkspace().getCurrentPageDescription();
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8030522..05336f2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
+import android.app.Person;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -97,6 +98,9 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
+    public static final String[] EMPTY_STRING_ARRAY = new String[0];
+    public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
+
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
     public static final boolean ATLEAST_P =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8cd0822..3be91d4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -81,6 +81,7 @@
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -420,6 +421,9 @@
         }
 
         // Always enter the spring loaded mode
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Switching to SPRING_LOADED");
+        }
         mLauncher.getStateManager().goToState(SPRING_LOADED);
     }
 
@@ -1053,8 +1057,11 @@
             // Not announcing the overlay page for accessibility since it announces itself.
         } else if (Float.compare(scroll, 0f) == 0) {
             if (mOverlayShown) {
-                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+                UserEventDispatcher ued = mLauncher.getUserEventDispatcher();
+                if (!ued.isPreviousHomeGesture()) {
+                    mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+                }
             } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
                 // When arriving to 0 overscroll from non-zero overscroll, announce page for
                 // accessibility since default announcements were disabled while in overscroll
@@ -1738,6 +1745,9 @@
     public void prepareAccessibilityDrop() { }
 
     public void onDrop(final DragObject d, DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDrop");
+        }
         mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
         CellLayout dropTargetLayout = mDropToLayout;
 
@@ -2415,6 +2425,9 @@
      * to add an item to one of the workspace screens.
      */
     private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDropExternal");
+        }
         if (d.dragInfo instanceof PendingAddShortcutInfo) {
             WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
                     .activityInfo.createWorkspaceItemInfo();
@@ -3248,6 +3261,10 @@
         }
     }
 
+    public boolean isOverlayShown() {
+        return mOverlayShown;
+    }
+
     void moveToDefaultScreen() {
         int page = DEFAULT_PAGE;
         if (!workspaceInModalState() && getNextPage() != page) {
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 5a2373b..b72866c 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -16,17 +16,23 @@
 
 package com.android.launcher3;
 
+import android.app.Person;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.ContentWriter;
 
+import java.util.Arrays;
+
 /**
  * Represents a launchable icon on the workspaces and in folders.
  */
@@ -83,10 +89,17 @@
     public int status;
 
     /**
+     * A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
+     * represents a deep shortcut.
+     */
+    @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
+
+    /**
      * The installation progress [0-100] of the package that this shortcut represents.
      */
     private int mInstallProgress;
 
+
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
@@ -98,6 +111,7 @@
         iconResource = info.iconResource;
         status = info.status;
         mInstallProgress = info.mInstallProgress;
+        personKeys = info.personKeys.clone();
     }
 
     /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
@@ -175,6 +189,10 @@
             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
         }
         disabledMessage = shortcutInfo.getDisabledMessage();
+
+        Person[] persons = UiFactory.getPersons(shortcutInfo);
+        personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
+            : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
     }
 
     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
@@ -183,6 +201,11 @@
                 getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
     }
 
+    @NonNull
+    public String[] getPersonKeys() {
+        return personKeys;
+    }
+
     @Override
     public ComponentName getTargetComponent() {
         ComponentName cn = super.getTargetComponent();
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index fd4df52..0c1303b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
@@ -115,7 +116,7 @@
 
         // If the request came from keyboard, do not add custom shortcuts as that is already
         // exposed as a direct shortcut
-        if (!fromKeyboard && DeepShortcutManager.supportsShortcuts(item)) {
+        if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
             info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
                     ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 160042e..a505240 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -26,8 +29,7 @@
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -45,27 +47,33 @@
     public static final int DEFER_UPDATES_TEST = 1 << 2;
 
     private PackageUserKey mTempKey = new PackageUserKey(null, null);
-    private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+    private AppInfo mTempInfo = new AppInfo();
+
+    private AppInfo[] mApps = EMPTY_ARRAY;
+
     private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
     private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
 
     private int mDeferUpdatesFlags = 0;
     private boolean mUpdatePending = false;
 
-    public Collection<AppInfo> getApps() {
-        return mComponentToAppMap.values();
+    public AppInfo[] getApps() {
+        return mApps;
     }
 
     /**
      * Sets the current set of apps.
      */
-    public void setApps(List<AppInfo> apps) {
-        mComponentToAppMap.clear();
-        addOrUpdateApps(apps);
+    public void setApps(AppInfo[] apps) {
+        mApps = apps;
+        notifyUpdate();
     }
 
     public AppInfo getApp(ComponentKey key) {
-        return mComponentToAppMap.get(key);
+        mTempInfo.componentName = key.componentName;
+        mTempInfo.user = key.user;
+        int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
+        return index < 0 ? null : mApps[index];
     }
 
     public void enableDeferUpdates(int flag) {
@@ -80,31 +88,14 @@
         }
     }
 
+    public void disableDeferUpdatesSilently(int flag) {
+        mDeferUpdatesFlags &= ~flag;
+    }
+
     public int getDeferUpdatesFlags() {
         return mDeferUpdatesFlags;
     }
 
-    /**
-     * Adds or updates existing apps in the list
-     */
-    public void addOrUpdateApps(List<AppInfo> apps) {
-        for (AppInfo app : apps) {
-            mComponentToAppMap.put(app.toComponentKey(), app);
-        }
-        notifyUpdate();
-    }
-
-    /**
-     * Removes some apps from the list.
-     */
-    public void removeApps(List<AppInfo> apps) {
-        for (AppInfo app : apps) {
-            mComponentToAppMap.remove(app.toComponentKey());
-        }
-        notifyUpdate();
-    }
-
-
     private void notifyUpdate() {
         if (mDeferUpdatesFlags != 0) {
             mUpdatePending = true;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a64374b..5b3beec 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -7,6 +7,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -208,13 +209,14 @@
         PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
                 : config.getPropertySetter(builder);
         boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
-        boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0;
+        boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
         Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
-        setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, allAppsFade);
-        setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, allAppsFade);
-        mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter,
-                allAppsFade);
+        Interpolator headerFade = builder.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
+        setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
+        setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
+        mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasAllAppsContent,
+                setter, headerFade, allAppsFade);
         mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
 
         setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 922e4f1..f899587 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -46,8 +46,8 @@
      */
     boolean hasVisibleContent();
 
-    void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
-            PropertySetter setter, Interpolator fadeInterpolator);
+    void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade);
 
     /**
      * Scrolls the content vertically.
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 66dced9..42a0eee 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -27,6 +27,10 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
@@ -40,10 +44,6 @@
 import java.util.ArrayList;
 import java.util.Map;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-
 public class FloatingHeaderView extends LinearLayout implements
         ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
         OnHeightUpdatedListener {
@@ -363,14 +363,14 @@
         p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
     }
 
-    public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
-            Interpolator fadeInterpolator) {
+    public void setContentVisibility(boolean hasHeader, boolean hasAllAppsContent,
+            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
         for (FloatingHeaderRow row : mAllRows) {
-            row.setContentVisibility(hasHeader, hasContent, setter, fadeInterpolator);
+            row.setContentVisibility(hasHeader, hasAllAppsContent, setter, headerFade, allAppsFade);
         }
 
-        allowTouchForwarding(hasContent);
-        setter.setFloat(mTabLayout, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+        allowTouchForwarding(hasAllAppsContent);
+        setter.setFloat(mTabLayout, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
     }
 
     protected void allowTouchForwarding(boolean allow) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index b283ff4..535ef54 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -64,10 +64,10 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
-            PropertySetter setter, Interpolator fadeInterpolator) {
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
         // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(mView, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+        setter.setFloat(mView, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
     }
 
     @Override
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 52a896e..cd30dea 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -39,6 +39,8 @@
     public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
     public static final int ANIM_OVERVIEW_FADE = 9;
     public static final int ANIM_ALL_APPS_FADE = 10;
+    public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
+    public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
 
     public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
 
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 8443231..c45cd85 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,8 +16,9 @@
 
 package com.android.launcher3.anim;
 
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
+import android.content.Context;
 import android.graphics.Path;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
@@ -188,13 +189,13 @@
          * @param totalDistancePx The distance against which progress is calculated.
          */
         public OvershootParams(float startProgress, float overshootPastProgress,
-                float endProgress, float velocityPxPerMs, int totalDistancePx) {
+                float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
             velocityPxPerMs = Math.abs(velocityPxPerMs);
             start = startProgress;
             int startPx = (int) (start * totalDistancePx);
             // Overshoot by about half a frame.
             float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs *
-                    SINGLE_FRAME_MS / totalDistancePx / 2;
+                    getSingleFrameMs(context) / totalDistancePx / 2;
             overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f);
             end = overshootPastProgress + overshootBy;
             int endPx = (int) (end  * totalDistancePx);
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 7dad7e9..4f4d641 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInstaller;
+import android.os.UserHandle;
 
 import java.util.HashMap;
 import java.util.List;
@@ -48,6 +49,11 @@
      */
     public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
 
+    /**
+     * @return an active SessionInfo for {@param pkg} or null if none exists.
+     */
+    public abstract PackageInstaller.SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
+
     public abstract void onStop();
 
     public static final class PackageInstallInfo {
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index a34ca50..8a5eabc 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -64,9 +64,9 @@
     @Override
     public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
         HashMap<String, SessionInfo> activePackages = new HashMap<>();
-        UserHandle user = Process.myUserHandle();
+        UserHandle primaryUser = Process.myUserHandle();
         for (SessionInfo info : getAllVerifiedSessions()) {
-            addSessionInfoToCache(info, user);
+            addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
             if (info.getAppPackageName() != null) {
                 activePackages.put(info.getAppPackageName(), info);
                 mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
@@ -75,6 +75,19 @@
         return activePackages;
     }
 
+    public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
+        for (SessionInfo info : getAllVerifiedSessions()) {
+            boolean match = pkg.equals(info.getAppPackageName());
+            if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) {
+                match = false;
+            }
+            if (match) {
+                return info;
+            }
+        }
+        return null;
+    }
+
     @Thunk void addSessionInfoToCache(SessionInfo info, UserHandle user) {
         String packageName = info.getAppPackageName();
         if (packageName != null) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index a72089d..a2dcbf8 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -153,16 +153,6 @@
         PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
                 img.getBitmap().getWidth(), img.getWidth());
 
-        Intent homeIntent = listener.addToIntent(
-                new Intent(Intent.ACTION_MAIN)
-                        .addCategory(Intent.CATEGORY_HOME)
-                        .setPackage(getPackageName())
-                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-
-        listener.initWhenReady();
-        startActivity(homeIntent,
-                ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
-        mFinishOnPause = true;
 
         // Start a system drag and drop. We use a transparent bitmap as preview for system drag
         // as the preview is handled internally by launcher.
@@ -179,6 +169,18 @@
                 outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
             }
         }, null, View.DRAG_FLAG_GLOBAL);
+
+
+        Intent homeIntent = listener.addToIntent(
+                new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Intent.CATEGORY_HOME)
+                        .setPackage(getPackageName())
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+
+        listener.initWhenReady();
+        startActivity(homeIntent,
+                ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+        mFinishOnPause = true;
         return false;
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d32dd2e..b72fd98 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -579,6 +579,9 @@
     }
 
     private void drop(DropTarget dropTarget, Runnable flingAnimation) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragController.drop");
+        }
         final int[] coordinates = mCoordinatesTemp;
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 84fc94d..bd2a03b 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -17,10 +17,12 @@
 package com.android.launcher3.dragndrop;
 
 import android.content.Context;
+import android.util.Log;
 import android.view.DragEvent;
 import android.view.MotionEvent;
 
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Base class for driving a drag/drop operation.
@@ -52,6 +54,9 @@
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_UP:
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_UP");
+                }
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
                 mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
                 break;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6ba015b..b59164a 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -48,12 +48,13 @@
 import com.android.launcher3.DropTargetBar;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.uioverrides.UiFactory;
@@ -92,7 +93,8 @@
 
     // Related to adjacent page hints
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
-    private final WorkspaceAndHotseatScrim mScrim;
+    private final WorkspaceAndHotseatScrim mWorkspaceScrim;
+    private final OverviewScrim mOverviewScrim;
 
     /**
      * Used to create a new DragLayer from XML.
@@ -108,12 +110,13 @@
         setChildrenDrawingOrderEnabled(true);
 
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
-        mScrim = new WorkspaceAndHotseatScrim(this);
+        mWorkspaceScrim = new WorkspaceAndHotseatScrim(this);
+        mOverviewScrim = new OverviewScrim(this);
     }
 
     public void setup(DragController dragController, Workspace workspace) {
         mDragController = dragController;
-        mScrim.setWorkspace(workspace);
+        mWorkspaceScrim.setWorkspace(workspace);
         recreateControllers();
     }
 
@@ -529,25 +532,39 @@
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the background below children.
-        mScrim.draw(canvas);
+        mWorkspaceScrim.draw(canvas);
+        mOverviewScrim.updateCurrentScrimmedView(this);
         mFocusIndicatorHelper.draw(canvas);
         super.dispatchDraw(canvas);
     }
 
     @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        if (child == mOverviewScrim.getScrimmedView()) {
+            mOverviewScrim.draw(canvas);
+        }
+        return super.drawChild(canvas, child, drawingTime);
+    }
+
+    @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mScrim.setSize(w, h);
+        mWorkspaceScrim.setSize(w, h);
     }
 
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
-        mScrim.onInsetsChanged(insets);
+        mWorkspaceScrim.onInsetsChanged(insets);
+        mOverviewScrim.onInsetsChanged(insets);
     }
 
     public WorkspaceAndHotseatScrim getScrim() {
-        return mScrim;
+        return mWorkspaceScrim;
+    }
+
+    public OverviewScrim getOverviewScrim() {
+        return mOverviewScrim;
     }
 
     @Override
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 0c5a1fc..d8a1f99 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -28,6 +28,8 @@
 import android.os.Build;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
@@ -66,15 +68,19 @@
         return mBadge;
     }
 
-    public static FolderAdaptiveIcon createFolderAdaptiveIcon(
+    public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
             Launcher launcher, int folderId, Point dragViewSize) {
         Preconditions.assertNonUiThread();
         int margin = launcher.getResources()
                 .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
 
         // Allocate various bitmaps on the background thread, because why not!
-        final Bitmap badge = Bitmap.createBitmap(
-                dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
+        int width = dragViewSize.x - margin;
+        int height = dragViewSize.y - margin;
+        if (width <= 0 || height <= 0) {
+            return null;
+        }
+        final Bitmap badge = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 
         // Create the actual drawable on the UI thread to avoid race conditions with
         // FolderIcon draw pass
diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java
new file mode 100644
index 0000000..d707403
--- /dev/null
+++ b/src/com/android/launcher3/graphics/OverviewScrim.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * View scrim which draws behind overview (recent apps).
+ */
+public class OverviewScrim extends Scrim {
+
+    private @NonNull View mStableScrimmedView;
+    // Might be higher up if mStableScrimmedView is invisible.
+    private @Nullable View mCurrentScrimmedView;
+
+    public OverviewScrim(View view) {
+        super(view);
+        mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
+
+        onExtractedColorsChanged(mWallpaperColorInfo);
+    }
+
+    public void onInsetsChanged(Rect insets) {
+        mStableScrimmedView = (OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0
+                ? mLauncher.getHotseat()
+                : mLauncher.getOverviewPanel();
+    }
+
+    public void updateCurrentScrimmedView(ViewGroup root) {
+        // Find the lowest view that is at or above the view we want to show the scrim behind.
+        mCurrentScrimmedView = mStableScrimmedView;
+        int currentIndex = root.indexOfChild(mCurrentScrimmedView);
+        final int childCount = root.getChildCount();
+        while (mCurrentScrimmedView.getVisibility() != VISIBLE && currentIndex < childCount) {
+            currentIndex++;
+            mCurrentScrimmedView = root.getChildAt(currentIndex);
+        }
+    }
+
+    /**
+     * @return The view to draw the scrim behind.
+     */
+    public View getScrimmedView() {
+        return mCurrentScrimmedView;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java
new file mode 100644
index 0000000..5c14f8d
--- /dev/null
+++ b/src/com/android/launcher3/graphics/Scrim.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.graphics.Canvas;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.uioverrides.WallpaperColorInfo;
+
+/**
+ * Contains general scrim properties such as wallpaper-extracted color that subclasses can use.
+ */
+public class Scrim implements View.OnAttachStateChangeListener,
+        WallpaperColorInfo.OnChangeListener {
+
+    public static Property<Scrim, Float> SCRIM_PROGRESS =
+            new Property<Scrim, Float>(Float.TYPE, "scrimProgress") {
+                @Override
+                public Float get(Scrim scrim) {
+                    return scrim.mScrimProgress;
+                }
+
+                @Override
+                public void set(Scrim scrim, Float value) {
+                    scrim.setScrimProgress(value);
+                }
+            };
+
+    protected final Launcher mLauncher;
+    protected final WallpaperColorInfo mWallpaperColorInfo;
+    protected final View mRoot;
+
+    protected float mScrimProgress;
+    protected int mScrimColor;
+    protected int mScrimAlpha = 0;
+
+    public Scrim(View view) {
+        mRoot = view;
+        mLauncher = Launcher.getLauncher(view.getContext());
+        mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+
+        view.addOnAttachStateChangeListener(this);
+    }
+
+    public void draw(Canvas canvas) {
+        canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha));
+    }
+
+    private void setScrimProgress(float progress) {
+        if (mScrimProgress != progress) {
+            mScrimProgress = progress;
+            mScrimAlpha = Math.round(255 * mScrimProgress);
+            invalidate();
+        }
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View view) {
+        mWallpaperColorInfo.addOnChangeListener(this);
+        onExtractedColorsChanged(mWallpaperColorInfo);
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View view) {
+        mWallpaperColorInfo.removeOnChangeListener(this);
+    }
+
+    @Override
+    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+        mScrimColor = wallpaperColorInfo.getMainColor();
+        if (mScrimAlpha > 0) {
+            invalidate();
+        }
+    }
+
+    public void invalidate() {
+        mRoot.invalidate();
+    }
+}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index c0aa75f..6740fa1 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -40,34 +40,19 @@
 import android.util.Property;
 import android.view.View;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.util.Themes;
 
-import androidx.core.graphics.ColorUtils;
-
 /**
  * View scrim which draws behind hotseat and workspace
  */
-public class WorkspaceAndHotseatScrim implements
-        View.OnAttachStateChangeListener, WallpaperColorInfo.OnChangeListener {
-
-    public static Property<WorkspaceAndHotseatScrim, Float> SCRIM_PROGRESS =
-            new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "scrimProgress") {
-                @Override
-                public Float get(WorkspaceAndHotseatScrim scrim) {
-                    return scrim.mScrimProgress;
-                }
-
-                @Override
-                public void set(WorkspaceAndHotseatScrim scrim, Float value) {
-                    scrim.setScrimProgress(value);
-                }
-            };
+public class WorkspaceAndHotseatScrim extends Scrim {
 
     public static Property<WorkspaceAndHotseatScrim, Float> SYSUI_PROGRESS =
             new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiProgress") {
@@ -117,9 +102,6 @@
     private static final int ALPHA_MASK_WIDTH_DP = 2;
 
     private final Rect mHighlightRect = new Rect();
-    private final Launcher mLauncher;
-    private final WallpaperColorInfo mWallpaperColorInfo;
-    private final View mRoot;
 
     private Workspace mWorkspace;
 
@@ -132,11 +114,6 @@
 
     private final Drawable mTopScrim;
 
-    private int mFullScrimColor;
-
-    private float mScrimProgress;
-    private int mScrimAlpha = 0;
-
     private float mSysUiProgress = 1;
     private boolean mHideSysUiScrim;
 
@@ -144,9 +121,7 @@
     private float mSysUiAnimMultiplier = 1;
 
     public WorkspaceAndHotseatScrim(View view) {
-        mRoot = view;
-        mLauncher = Launcher.getLauncher(view.getContext());
-        mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+        super(view);
 
         mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
                 view.getResources().getDisplayMetrics());
@@ -154,7 +129,6 @@
         mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask();
         mHideSysUiScrim = mTopScrim == null;
 
-        view.addOnAttachStateChangeListener(this);
         onExtractedColorsChanged(mWallpaperColorInfo);
     }
 
@@ -176,7 +150,7 @@
                 canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
             }
 
-            canvas.drawColor(setColorAlphaBound(mFullScrimColor, mScrimAlpha));
+            super.draw(canvas);
             canvas.restore();
         }
 
@@ -190,11 +164,8 @@
                 mSysUiAnimMultiplier = 0;
                 reapplySysUiAlphaNoInvalidate();
 
-                ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, 1);
-                anim.setAutoCancel(true);
-                anim.setDuration(600);
-                anim.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
-                anim.start();
+                animateToSysuiMultiplier(1, 600,
+                        mLauncher.getWindow().getTransitionBackgroundFadeDuration());
                 mAnimateScrimOnNextDraw = false;
             }
 
@@ -207,24 +178,24 @@
         }
     }
 
+    public void animateToSysuiMultiplier(float toMultiplier, long duration,
+            long startDelay) {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, toMultiplier);
+        anim.setAutoCancel(true);
+        anim.setDuration(duration);
+        anim.setStartDelay(startDelay);
+        anim.start();
+    }
+
     public void onInsetsChanged(Rect insets) {
         mDrawTopScrim = mTopScrim != null && insets.top > 0;
         mDrawBottomScrim = mBottomMask != null &&
                 !mLauncher.getDeviceProfile().isVerticalBarLayout();
     }
 
-    private void setScrimProgress(float progress) {
-        if (mScrimProgress != progress) {
-            mScrimProgress = progress;
-            mScrimAlpha = Math.round(255 * mScrimProgress);
-            invalidate();
-        }
-    }
-
     @Override
     public void onViewAttachedToWindow(View view) {
-        mWallpaperColorInfo.addOnChangeListener(this);
-        onExtractedColorsChanged(mWallpaperColorInfo);
+        super.onViewAttachedToWindow(view);
 
         if (mTopScrim != null) {
             IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
@@ -235,7 +206,7 @@
 
     @Override
     public void onViewDetachedFromWindow(View view) {
-        mWallpaperColorInfo.removeOnChangeListener(this);
+        super.onViewDetachedFromWindow(view);
         if (mTopScrim != null) {
             mRoot.getContext().unregisterReceiver(mReceiver);
         }
@@ -248,10 +219,7 @@
         mBottomMaskPaint.setColor(ColorUtils.compositeColors(DARK_SCRIM_COLOR,
                 wallpaperColorInfo.getMainColor()));
         reapplySysUiAlpha();
-        mFullScrimColor = wallpaperColorInfo.getMainColor();
-        if (mScrimAlpha > 0) {
-            invalidate();
-        }
+        super.onExtractedColorsChanged(wallpaperColorInfo);
     }
 
     public void setSize(int w, int h) {
@@ -291,10 +259,6 @@
         }
     }
 
-    public void invalidate() {
-        mRoot.invalidate();
-    }
-
     public Bitmap createDitheredAlphaMask() {
         DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
         int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 647f255..b02a050 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -1,9 +1,12 @@
 package com.android.launcher3.logging;
 
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
+
 import android.view.View;
 import android.view.ViewParent;
 
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 import androidx.annotation.Nullable;
@@ -64,4 +67,20 @@
         }
         return null;
     }
+
+    public static int getContainerTypeFromState(int state) {
+        int containerType = DEFAULT_CONTAINERTYPE;
+        switch (state) {
+            case StatsLogUtils.LAUNCHER_STATE_ALLAPPS:
+                containerType = ContainerType.ALLAPPS;
+                break;
+            case StatsLogUtils.LAUNCHER_STATE_HOME:
+                containerType = ContainerType.WORKSPACE;
+                break;
+            case StatsLogUtils.LAUNCHER_STATE_OVERVIEW:
+                containerType = ContainerType.OVERVIEW;
+                break;
+        }
+        return containerType;
+    }
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index bd785a1..c72b07a 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -115,6 +115,7 @@
     protected InstantAppResolver mInstantAppResolver;
     private boolean mAppOrTaskLaunch;
     private UserEventDelegate mDelegate;
+    private boolean mPreviousHomeGesture;
 
     //                      APP_ICON    SHORTCUT    WIDGET
     // --------------------------------------------------------------
@@ -186,6 +187,14 @@
                 dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
     }
 
+    public void logActionCommand(int command, int srcContainerType, int dstContainerType,
+                                 int pageIndex) {
+        Target srcTarget = newContainerTarget(srcContainerType);
+        srcTarget.pageIndex = pageIndex;
+        logActionCommand(command, srcTarget,
+                dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+    }
+
     public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
         LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
         if (command == Action.Command.STOP) {
@@ -399,11 +408,22 @@
         mElapsedContainerMillis = SystemClock.uptimeMillis();
     }
 
+    public final void setPreviousHomeGesture(boolean homeGesture) {
+        mPreviousHomeGesture = homeGesture;
+    }
+
+    public final boolean isPreviousHomeGesture() {
+        return mPreviousHomeGesture;
+    }
+
     public final void resetActionDurationMillis() {
         mActionDurationMillis = SystemClock.uptimeMillis();
     }
 
     public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+        if (mPreviousHomeGesture) {
+            mPreviousHomeGesture = false;
+        }
         mAppOrTaskLaunch = false;
         ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
         ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
@@ -426,6 +446,7 @@
                 ev.actionDurationMillis);
         log += "\n\n";
         Log.d(TAG, log);
+        return;
     }
 
     private static String getTargetsStr(Target[] targets) {
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 8c264c1..935326d 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -20,7 +20,6 @@
 import android.util.LongSparseArray;
 import android.util.Pair;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -28,7 +27,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.util.GridOccupancy;
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
similarity index 73%
rename from src/com/android/launcher3/AllAppsList.java
rename to src/com/android/launcher3/model/AllAppsList.java
index 733f295..2fddbaa 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model;
+
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,15 +27,22 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.SafeCloseable;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.function.Consumer;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -42,23 +52,22 @@
  * Stores the list of all applications for the all apps view.
  */
 public class AllAppsList {
+
     private static final String TAG = "AllAppsList";
+    private static final Consumer<AppInfo> NO_OP_CONSUMER = a -> { };
+
 
     public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
 
     /** The list off all apps. */
     public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
-    /** The list of apps that have been added since the last notify() call. */
-    public ArrayList<AppInfo> added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
-    /** The list of apps that have been removed since the last notify() call. */
-    public ArrayList<AppInfo> removed = new ArrayList<>();
-    /** The list of apps that have been modified since the last notify() call. */
-    public ArrayList<AppInfo> modified = new ArrayList<>();
 
     private IconCache mIconCache;
-
     private AppFilter mAppFilter;
 
+    private boolean mDataChanged = false;
+    private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
+
     /**
      * Boring constructor.
      */
@@ -68,6 +77,15 @@
     }
 
     /**
+     * Returns true if there have been any changes since last call.
+     */
+    public boolean getAndResetChangeFlag() {
+        boolean result = mDataChanged;
+        mDataChanged = false;
+        return result;
+    }
+
+    /**
      * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
      * list to broadcast when notify() is called.
      *
@@ -83,7 +101,7 @@
         mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
 
         data.add(info);
-        added.add(info);
+        mDataChanged = true;
     }
 
     public void addPromiseApp(Context context,
@@ -95,30 +113,41 @@
             PromiseAppInfo info = new PromiseAppInfo(installInfo);
             mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
             data.add(info);
-            added.add(info);
+            mDataChanged = true;
         }
     }
 
-    public void removePromiseApp(AppInfo appInfo) {
-        // the <em>removed</em> list is handled by the caller
-        // so not adding it here
-        data.remove(appInfo);
+    public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+        UserHandle user = Process.myUserHandle();
+        for (int i=0; i < data.size(); i++) {
+            final AppInfo appInfo = data.get(i);
+            final ComponentName tgtComp = appInfo.getTargetComponent();
+            if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
+                    && appInfo.user.equals(user)
+                    && appInfo instanceof PromiseAppInfo) {
+                final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+                if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+                    promiseAppInfo.level = installInfo.progress;
+                    return promiseAppInfo;
+                } else if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+                    removeApp(i);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void removeApp(int index) {
+        AppInfo removed = data.remove(index);
+        if (removed != null) {
+            mDataChanged = true;
+            mRemoveListener.accept(removed);
+        }
     }
 
     public void clear() {
         data.clear();
-        // TODO: do we clear these too?
-        added.clear();
-        removed.clear();
-        modified.clear();
-    }
-
-    public int size() {
-        return data.size();
-    }
-
-    public AppInfo get(int index) {
-        return data.get(index);
+        mDataChanged = false;
     }
 
     /**
@@ -142,8 +171,7 @@
         for (int i = data.size() - 1; i >= 0; i--) {
             AppInfo info = data.get(i);
             if (info.user.equals(user) && packageName.equals(info.componentName.getPackageName())) {
-                removed.add(info);
-                data.remove(i);
+                removeApp(i);
             }
         }
     }
@@ -157,17 +185,16 @@
             AppInfo info = data.get(i);
             if (matcher.matches(info, info.componentName)) {
                 info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags);
-                modified.add(info);
+                mDataChanged = true;
             }
         }
     }
 
-    public void updateIconsAndLabels(HashSet<String> packages, UserHandle user,
-            ArrayList<AppInfo> outUpdates) {
+    public void updateIconsAndLabels(HashSet<String> packages, UserHandle user) {
         for (AppInfo info : data) {
             if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) {
                 mIconCache.updateTitleAndIcon(info);
-                outUpdates.add(info);
+                mDataChanged = true;
             }
         }
     }
@@ -188,8 +215,7 @@
                         && packageName.equals(applicationInfo.componentName.getPackageName())) {
                     if (!findActivity(matches, applicationInfo.componentName)) {
                         Log.w(TAG, "Changing shortcut target due to app component name change.");
-                        removed.add(applicationInfo);
-                        data.remove(i);
+                        removeApp(i);
                     }
                 }
             }
@@ -202,7 +228,7 @@
                     add(new AppInfo(context, info, user), info);
                 } else {
                     mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
-                    modified.add(applicationInfo);
+                    mDataChanged = true;
                 }
             }
         } else {
@@ -211,15 +237,13 @@
                 final AppInfo applicationInfo = data.get(i);
                 if (user.equals(applicationInfo.user)
                         && packageName.equals(applicationInfo.componentName.getPackageName())) {
-                    removed.add(applicationInfo);
                     mIconCache.remove(applicationInfo.componentName, user);
-                    data.remove(i);
+                    removeApp(i);
                 }
             }
         }
     }
 
-
     /**
      * Returns whether <em>apps</em> contains <em>component</em>.
      */
@@ -247,4 +271,16 @@
         }
         return null;
     }
+
+    public AppInfo[] copyData() {
+        AppInfo[] result = data.toArray(EMPTY_ARRAY);
+        Arrays.sort(result, COMPONENT_KEY_COMPARATOR);
+        return result;
+    }
+
+    public SafeCloseable trackRemoves(Consumer<AppInfo> removeListener) {
+        mRemoveListener = removeListener;
+
+        return () -> mRemoveListener = NO_OP_CONSUMER;
+    }
 }
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 97cf267..018f93a 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -19,14 +19,13 @@
 import android.os.Looper;
 import android.util.Log;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.PagedView;
@@ -279,9 +278,8 @@
 
     public void bindAllApps() {
         // shallow copy
-        @SuppressWarnings("unchecked")
-        ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
-        executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor);
+        AppInfo[] apps = mBgAllAppsList.copyData();
+        executeCallbacksTask(c -> c.bindAllApplications(apps), mUiExecutor);
     }
 
     public abstract void bindWidgets();
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index eea3d8c..e12633b 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,12 +17,12 @@
 
 import android.util.Log;
 
-import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -30,6 +30,7 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -95,12 +96,7 @@
 
     public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
         if (!updatedShortcuts.isEmpty()) {
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindWorkspaceItemsChanged(updatedShortcuts);
-                }
-            });
+            scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
         }
     }
 
@@ -113,23 +109,20 @@
     public void bindUpdatedWidgets(BgDataModel dataModel) {
         final ArrayList<WidgetListRowEntry> widgets =
                 dataModel.widgetsModel.getWidgetsList(mApp.getContext());
-        scheduleCallbackTask(new CallbackTask() {
-            @Override
-            public void execute(Callbacks callbacks) {
-                callbacks.bindAllWidgets(widgets);
-            }
-        });
+        scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
     }
 
     public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
         getModelWriter().deleteItemsFromDatabase(matcher);
 
         // Call the components-removed callback
-        scheduleCallbackTask(new CallbackTask() {
-            @Override
-            public void execute(Callbacks callbacks) {
-                callbacks.bindWorkspaceComponentsRemoved(matcher);
-            }
-        });
+        scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher));
+    }
+
+    public void bindApplicationsIfNeeded() {
+        if (mAllAppsList.getAndResetChangeFlag()) {
+            AppInfo[] apps = mAllAppsList.copyData();
+            scheduleCallbackTask(c -> c.bindAllApplications(apps));
+        }
     }
 }
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8f0cd08..0e20270 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -22,11 +22,13 @@
 import android.util.Log;
 import android.util.MutableInt;
 
+import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
@@ -40,6 +42,10 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
 import com.google.protobuf.nano.MessageNano;
 
 import java.io.FileDescriptor;
@@ -49,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -391,4 +398,30 @@
             }
         }
     }
+
+    public interface Callbacks {
+        void rebindModel();
+
+        int getCurrentWorkspaceScreen();
+        void clearPendingBinds();
+        void startBinding();
+        void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
+        void bindScreens(IntArray orderedScreenIds);
+        void finishFirstPageBind(ViewOnDrawExecutor executor);
+        void finishBindingItems(int pageBoundFirst);
+        void preAddApps();
+        void bindAppsAdded(IntArray newScreens,
+                ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
+        void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+        void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+        void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
+        void bindRestoreItemsChange(HashSet<ItemInfo> updates);
+        void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
+        void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
+        void onPageBoundSynchronously(int page);
+        void executeOnNextDraw(ViewOnDrawExecutor executor);
+        void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
+
+        void bindAllApplications(AppInfo[] apps);
+    }
 }
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 7852444..c1c8be3 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,14 +18,10 @@
 import android.content.ComponentName;
 import android.os.UserHandle;
 
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
 
 import java.util.ArrayList;
@@ -53,9 +49,9 @@
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         IconCache iconCache = app.getIconCache();
 
-        final ArrayList<AppInfo> updatedApps = new ArrayList<>();
 
         ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
+
         synchronized (dataModel) {
             for (ItemInfo info : dataModel.itemsIdMap) {
                 if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
@@ -69,18 +65,10 @@
                     }
                 }
             }
-            apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
+            apps.updateIconsAndLabels(mPackages, mUser);
         }
         bindUpdatedWorkspaceItems(updatedShortcuts);
-
-        if (!updatedApps.isEmpty()) {
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindAppsAddedOrUpdated(updatedApps);
-                }
-            });
-        }
+        bindApplicationsIfNeeded();
     }
 
     public boolean isValidShortcut(WorkspaceItemInfo si) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 6c33946..19eaadb 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -40,7 +40,6 @@
 import android.util.LongSparseArray;
 import android.util.MutableInt;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
@@ -831,7 +830,7 @@
             }
         }
 
-        mBgAllAppsList.added = new ArrayList<>();
+        mBgAllAppsList.getAndResetChangeFlag();
         return allActivityList;
     }
 
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index b353810..2bd6cd4 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.util.Log;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4ce2f4b..d7ab0dd 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -31,7 +31,7 @@
 import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5f6d128..e43412d 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -20,20 +20,18 @@
 import android.content.pm.PackageManager;
 import android.os.Process;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
-import java.util.ArrayList;
 import java.util.HashSet;
 
 /**
@@ -66,41 +64,11 @@
         }
 
         synchronized (apps) {
-            PromiseAppInfo updated = null;
-            final ArrayList<AppInfo> removed = new ArrayList<>();
-            for (int i=0; i < apps.size(); i++) {
-                final AppInfo appInfo = apps.get(i);
-                final ComponentName tgtComp = appInfo.getTargetComponent();
-                if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
-                    if (appInfo instanceof PromiseAppInfo) {
-                        final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
-                        if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
-                            promiseAppInfo.level = mInstallInfo.progress;
-                            updated = promiseAppInfo;
-                        } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
-                            apps.removePromiseApp(appInfo);
-                            removed.add(appInfo);
-                        }
-                    }
-                }
-            }
+            PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
             if (updated != null) {
-                final PromiseAppInfo updatedPromiseApp = updated;
-                scheduleCallbackTask(new CallbackTask() {
-                    @Override
-                    public void execute(Callbacks callbacks) {
-                        callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
-                    }
-                });
+                scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
             }
-            if (!removed.isEmpty()) {
-                scheduleCallbackTask(new CallbackTask() {
-                    @Override
-                    public void execute(Callbacks callbacks) {
-                        callbacks.bindAppInfosRemoved(removed);
-                    }
-                });
-            }
+            bindApplicationsIfNeeded();
         }
 
         synchronized (dataModel) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c37ed99..17a9a02 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -23,23 +23,19 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -48,6 +44,7 @@
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SafeCloseable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -93,6 +90,8 @@
         FlagOp flagOp = FlagOp.NO_OP;
         final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
         ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+        final HashSet<ComponentName> removedComponents = new HashSet<>();
+
         switch (mOp) {
             case OP_ADD: {
                 for (int i = 0; i < N; i++) {
@@ -112,11 +111,14 @@
                 break;
             }
             case OP_UPDATE:
-                for (int i = 0; i < N; i++) {
-                    if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
-                    iconCache.updateIconsForPkg(packages[i], mUser);
-                    appsList.updatePackage(context, packages[i], mUser);
-                    app.getWidgetCache().removePackage(packages[i], mUser);
+                try (SafeCloseable t =
+                             appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
+                    for (int i = 0; i < N; i++) {
+                        if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
+                        iconCache.updateIconsForPkg(packages[i], mUser);
+                        appsList.updatePackage(context, packages[i], mUser);
+                        app.getWidgetCache().removePackage(packages[i], mUser);
+                    }
                 }
                 // Since package was just updated, the target must be available now.
                 flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
@@ -153,23 +155,7 @@
                 break;
         }
 
-        final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
-        addedOrModified.addAll(appsList.added);
-        appsList.added.clear();
-        addedOrModified.addAll(appsList.modified);
-        appsList.modified.clear();
-        if (!addedOrModified.isEmpty()) {
-            scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
-        }
-
-        final ArrayList<AppInfo> removedApps = new ArrayList<>(appsList.removed);
-        appsList.removed.clear();
-        final HashSet<ComponentName> removedComponents = new HashSet<>();
-        if (mOp == OP_UPDATE) {
-            for (AppInfo ai : removedApps) {
-                removedComponents.add(ai.componentName);
-            }
-        }
+        bindApplicationsIfNeeded();
 
         final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
 
@@ -296,12 +282,7 @@
             }
 
             if (!widgets.isEmpty()) {
-                scheduleCallbackTask(new CallbackTask() {
-                    @Override
-                    public void execute(Callbacks callbacks) {
-                        callbacks.bindWidgetsRestored(widgets);
-                    }
-                });
+                scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets));
             }
         }
 
@@ -332,16 +313,6 @@
             InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
         }
 
-        if (!removedApps.isEmpty()) {
-            // Remove corresponding apps from All-Apps
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindAppInfosRemoved(removedApps);
-                }
-            });
-        }
-
         if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
             // Load widgets for the new package. Changes due to app updates are handled through
             // AppWidgetHost events, this is just to initialize the long-press options.
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 8528228..c3cd9d0 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,7 +19,6 @@
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 50fff26..4b773d7 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,7 +21,6 @@
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index 5050457..bfa4ba9 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -17,12 +17,18 @@
 package com.android.launcher3.notification;
 
 import android.app.Notification;
+import android.app.Person;
 import android.service.notification.StatusBarNotification;
 
+import com.android.launcher3.Utilities;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * The key data associated with the notification, used to determine what to include
@@ -34,19 +40,25 @@
     public final String notificationKey;
     public final String shortcutId;
     public int count;
+    @NonNull public final String[] personKeysFromNotification;
 
-    private NotificationKeyData(String notificationKey, String shortcutId, int count) {
+    private NotificationKeyData(String notificationKey, String shortcutId, int count,
+            String[] personKeysFromNotification) {
         this.notificationKey = notificationKey;
         this.shortcutId = shortcutId;
         this.count = Math.max(1, count);
+        this.personKeysFromNotification = personKeysFromNotification;
     }
 
     public static NotificationKeyData fromNotification(StatusBarNotification sbn) {
         Notification notif = sbn.getNotification();
-        return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number);
+        return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number,
+                extractPersonKeyOnly(notif.extras.getParcelableArrayList(
+                        Notification.EXTRA_PEOPLE_LIST)));
     }
 
-    public static List<String> extractKeysOnly(@NonNull List<NotificationKeyData> notificationKeys) {
+    public static List<String> extractKeysOnly(
+            @NonNull List<NotificationKeyData> notificationKeys) {
         List<String> keysOnly = new ArrayList<>(notificationKeys.size());
         for (NotificationKeyData notificationKeyData : notificationKeys) {
             keysOnly.add(notificationKeyData.notificationKey);
@@ -54,6 +66,13 @@
         return keysOnly;
     }
 
+    private static String[] extractPersonKeyOnly(@Nullable ArrayList<Person> people) {
+        if (people == null || people.isEmpty()) {
+            return Utilities.EMPTY_STRING_ARRAY;
+        }
+        return people.stream().map(Person::getKey).sorted().toArray(String[]::new);
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof NotificationKeyData)) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 25d9f79..baaad65 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -72,6 +72,7 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
@@ -201,7 +202,7 @@
             return null;
         }
         ItemInfo itemInfo = (ItemInfo) icon.getTag();
-        if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
+        if (!ShortcutUtil.supportsShortcuts(itemInfo)) {
             return null;
         }
 
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 2d301ac..4612b2a 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -29,17 +29,22 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * Provides data for the popup menu that appears after long-clicking on apps.
@@ -129,7 +134,8 @@
         for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
             DotInfo prevDot = updatedDots.get(packageUserKey);
             DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
-            if (prevDot == null) {
+            if (prevDot == null
+                    || prevDot.getNotificationCount() != newDot.getNotificationCount()) {
                 updatedDots.put(packageUserKey, newDot);
             } else {
                 // No need to update the dot if it already existed (no visual change).
@@ -155,7 +161,7 @@
     }
 
     public int getShortcutCountForItem(ItemInfo info) {
-        if (!DeepShortcutManager.supportsShortcuts(info)) {
+        if (!ShortcutUtil.supportsDeepShortcuts(info)) {
             return 0;
         }
         ComponentName component = info.getTargetComponent();
@@ -167,17 +173,26 @@
         return count == null ? 0 : count;
     }
 
-    public DotInfo getDotInfoForItem(ItemInfo info) {
-        if (!DeepShortcutManager.supportsShortcuts(info)) {
+    public @Nullable DotInfo getDotInfoForItem(@NonNull ItemInfo info) {
+        if (!ShortcutUtil.supportsShortcuts(info)) {
             return null;
         }
-
-        return mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+        DotInfo dotInfo = mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+        if (dotInfo == null) {
+            return null;
+        }
+        List<NotificationKeyData> notifications = getNotificationsForItem(
+                info, dotInfo.getNotificationKeys());
+        if (notifications.isEmpty()) {
+            return null;
+        }
+        return dotInfo;
     }
 
     public @NonNull List<NotificationKeyData> getNotificationKeysForItem(ItemInfo info) {
         DotInfo dotInfo = getDotInfoForItem(info);
-        return dotInfo == null ? Collections.EMPTY_LIST : dotInfo.getNotificationKeys();
+        return dotInfo == null ? Collections.EMPTY_LIST
+                : getNotificationsForItem(info, dotInfo.getNotificationKeys());
     }
 
     /** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -226,6 +241,27 @@
         return null;
     }
 
+    /**
+     * Returns a list of notifications that are relevant to given ItemInfo.
+     */
+    public static @NonNull List<NotificationKeyData> getNotificationsForItem(
+            @NonNull ItemInfo info, @NonNull List<NotificationKeyData> notifications) {
+        String shortcutId = ShortcutUtil.getShortcutIdIfPinnedShortcut(info);
+        if (shortcutId == null) {
+            return notifications;
+        }
+        String[] personKeys = ShortcutUtil.getPersonKeysIfPinnedShortcut(info);
+        return notifications.stream().filter((NotificationKeyData notification) -> {
+                    if (notification.shortcutId != null) {
+                        return notification.shortcutId.equals(shortcutId);
+                    }
+                    if (notification.personKeysFromNotification.length != 0) {
+                        return Arrays.equals(notification.personKeysFromNotification, personKeys);
+                    }
+                    return false;
+                }).collect(Collectors.toList());
+    }
+
     public interface PopupDataChangeListener {
 
         PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 41ab4df..5a5fbab 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -29,11 +29,12 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
     private static final String TAG = "RemoteActionShortcut";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
 
     private final RemoteAction mAction;
 
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index c6370c5..446d4f8 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -22,7 +22,7 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.MainThreadExecutor;
 
 import java.lang.ref.WeakReference;
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 9846a04..cb8f811 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -79,4 +79,5 @@
     public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
 
     public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
+    public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index ae69f3b..c5ba5ba 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -22,9 +22,9 @@
 import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
 import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -55,8 +55,6 @@
 public abstract class AbstractStateChangeTouchController
         implements TouchController, SwipeDetector.Listener {
 
-    private static final String TAG = "ASCTouchController";
-
     // Progress after which the transition is assumed to be a success in case user does not fling
     public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
 
@@ -71,6 +69,7 @@
     protected final SwipeDetector.Direction mSwipeDirection;
 
     private boolean mNoIntercept;
+    private boolean mIsLogContainerSet;
     protected int mStartContainerType;
 
     protected LauncherState mStartState;
@@ -182,7 +181,7 @@
     /**
      * Returns the container that the touch started from when leaving NORMAL state.
      */
-    protected abstract int getLogContainerTypeForNormalState();
+    protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
 
     private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
         LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -233,13 +232,7 @@
     @Override
     public void onDragStart(boolean start) {
         mStartState = mLauncher.getStateManager().getState();
-        if (mStartState == ALL_APPS) {
-            mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
-        } else if (mStartState == NORMAL) {
-            mStartContainerType = getLogContainerTypeForNormalState();
-        } else if (mStartState == OVERVIEW){
-            mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
-        }
+        mIsLogContainerSet = false;
         if (mCurrentAnimation == null) {
             mFromState = mStartState;
             mToState = null;
@@ -287,6 +280,21 @@
         return true;
     }
 
+    @Override
+    public boolean onDrag(float displacement, MotionEvent ev) {
+        if (!mIsLogContainerSet) {
+            if (mStartState == ALL_APPS) {
+                mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
+            } else if (mStartState == NORMAL) {
+                mStartContainerType = getLogContainerTypeForNormalState(ev);
+            } else if (mStartState == OVERVIEW) {
+                mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+            }
+            mIsLogContainerSet = true;
+        }
+        return onDrag(displacement);
+    }
+
     protected void updateProgress(float fraction) {
         mCurrentAnimation.setPlayFraction(fraction);
         if (mAtomicComponentsController != null) {
@@ -396,8 +404,8 @@
                 duration = 0;
                 startProgress = 1;
             } else {
-                startProgress = Utilities.boundToRange(
-                        progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+                startProgress = Utilities.boundToRange(progress
+                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
                 duration = SwipeDetector.calculateDuration(velocity,
                         endProgress - Math.max(progress, 0)) * durationMultiplier;
             }
@@ -414,8 +422,8 @@
                 duration = 0;
                 startProgress = 0;
             } else {
-                startProgress = Utilities.boundToRange(
-                        progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+                startProgress = Utilities.boundToRange(progress
+                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
                 duration = SwipeDetector.calculateDuration(velocity,
                         Math.min(progress, 1) - endProgress) * durationMultiplier;
             }
@@ -520,6 +528,7 @@
             logReachedState(logAction, targetState);
         }
         mLauncher.getStateManager().goToState(targetState, false /* animated */);
+        mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0);
     }
 
     private void logReachedState(int logAction, LauncherState targetState) {
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 2895a89..03493a5 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -25,9 +25,15 @@
 import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
 
 import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.Process;
+import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Toast;
@@ -43,11 +49,12 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
@@ -58,6 +65,8 @@
  */
 public class ItemClickHandler {
 
+    private static final String TAG = ItemClickHandler.class.getSimpleName();
+
     /**
      * Instance used for click handling on items
      */
@@ -146,6 +155,8 @@
             startMarketIntentForPackage(v, launcher, packageName);
             return;
         }
+        UserHandle user = v.getTag() instanceof ItemInfo
+                ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
         new AlertDialog.Builder(launcher)
                 .setTitle(R.string.abandoned_promises_title)
                 .setMessage(R.string.abandoned_promise_explanation)
@@ -153,12 +164,28 @@
                         (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
                 .setNeutralButton(R.string.abandoned_clean_this,
                         (d, i) -> launcher.getWorkspace()
-                                .removeAbandonedPromise(packageName, Process.myUserHandle()))
+                                .removeAbandonedPromise(packageName, user))
                 .create().show();
     }
 
     private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
+        if (Utilities.ATLEAST_Q) {
+            PackageInstallerCompat pkgInstaller = PackageInstallerCompat.getInstance(launcher);
+            SessionInfo sessionInfo = pkgInstaller.getActiveSessionInfo(item.user, packageName);
+            if (sessionInfo != null) {
+                LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
+                try {
+                    launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
+                            launcher.getActivityLaunchOptionsAsBundle(v));
+                    return;
+                } catch (Exception e) {
+                    Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
+                }
+            }
+        }
+
+        // Fallback to using custom market intent.
         Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
         launcher.startActivitySafely(v, intent, item, null);
     }
diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
new file mode 100644
index 0000000..7719f08
--- /dev/null
+++ b/src/com/android/launcher3/util/DefaultDisplay.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to cache properties of default display to avoid a system RPC on every call.
+ */
+public class DefaultDisplay implements DisplayListener {
+
+    public static final MainThreadInitializedObject<DefaultDisplay> INSTANCE =
+            new MainThreadInitializedObject<>(DefaultDisplay::new);
+
+    private static final String TAG = "DefaultDisplay";
+
+    public static final int CHANGE_SIZE = 1 << 0;
+    public static final int CHANGE_ROTATION = 1 << 1;
+    public static final int CHANGE_FRAME_DELAY = 1 << 2;
+
+    private final Context mContext;
+    private final int mId;
+    private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+    private final Handler mChangeHandler;
+    private Info mInfo;
+
+    private DefaultDisplay(Context context) {
+        mContext = context;
+        mInfo = new Info(context);
+        mId = mInfo.id;
+        mChangeHandler = new Handler(this::onChange);
+
+        context.getSystemService(DisplayManager.class)
+                .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
+    }
+
+    @Override
+    public final void onDisplayAdded(int displayId) {  }
+
+    @Override
+    public final void onDisplayRemoved(int displayId) { }
+
+    @Override
+    public final void onDisplayChanged(int displayId) {
+        if (displayId != mId) {
+            return;
+        }
+
+        Info oldInfo = mInfo;
+        Info info = new Info(mContext);
+
+        int change = 0;
+        if (info.hasDifferentSize(oldInfo)) {
+            change |= CHANGE_SIZE;
+        }
+        if (oldInfo.rotation != info.rotation) {
+            change |= CHANGE_ROTATION;
+        }
+        if (info.singleFrameMs != oldInfo.singleFrameMs) {
+            change |= CHANGE_FRAME_DELAY;
+        }
+
+        if (change != 0) {
+            mInfo = info;
+            mChangeHandler.sendEmptyMessage(change);
+        }
+    }
+
+    public static int getSingleFrameMs(Context context) {
+        return INSTANCE.get(context).getInfo().singleFrameMs;
+    }
+
+    public Info getInfo() {
+        return mInfo;
+    }
+
+    public void addChangeListener(DisplayInfoChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeChangeListener(DisplayInfoChangeListener listener) {
+        mListeners.remove(listener);
+    }
+
+    private boolean onChange(Message msg) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            mListeners.get(i).onDisplayInfoChanged(mInfo, msg.what);
+        }
+        return true;
+    }
+
+    public static class Info {
+
+        public final int id;
+        public final int rotation;
+        public final int singleFrameMs;
+
+        public final Point realSize;
+        public final Point smallestSize;
+        public final Point largestSize;
+
+        private Info(Context context) {
+            Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
+
+            id = display.getDisplayId();
+            rotation = display.getRotation();
+
+            float refreshRate = display.getRefreshRate();
+            singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+
+            realSize = new Point();
+            smallestSize = new Point();
+            largestSize = new Point();
+            display.getRealSize(realSize);
+            display.getCurrentSizeRange(smallestSize, largestSize);
+        }
+
+        private boolean hasDifferentSize(Info info) {
+            if (!realSize.equals(info.realSize)
+                    && !realSize.equals(info.realSize.y, info.realSize.x)) {
+                Log.d(TAG, String.format("Display size changed from %s to %s",
+                        info.realSize, realSize));
+                return true;
+            }
+
+            if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
+                Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
+                        smallestSize, largestSize, info.smallestSize, info.largestSize));
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * Interface for listening for display changes
+     */
+    public interface DisplayInfoChangeListener {
+
+        void onDisplayInfoChanged(Info info, int flags);
+    }
+}
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index 1ce2822..e624517 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -38,7 +38,7 @@
      * @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
      */
     public boolean updateFromItemInfo(ItemInfo info) {
-        if (DeepShortcutManager.supportsShortcuts(info)) {
+        if (ShortcutUtil.supportsShortcuts(info)) {
             update(info.getTargetComponent().getPackageName(), info.user);
             return true;
         }
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/util/SafeCloseable.java
new file mode 100644
index 0000000..ba8ee04
--- /dev/null
+++ b/src/com/android/launcher3/util/SafeCloseable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+/**
+ * Extension of closeable which does not throw an exception
+ */
+public interface SafeCloseable extends AutoCloseable {
+
+    @Override
+    void close();
+}
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
new file mode 100644
index 0000000..792d69f
--- /dev/null
+++ b/src/com/android/launcher3/util/ShortcutUtil.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.launcher3.util;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+public class ShortcutUtil {
+  public static boolean supportsShortcuts(ItemInfo info) {
+    return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+  }
+
+  public static boolean supportsDeepShortcuts(ItemInfo info) {
+    return isActive(info) && isApp(info);
+  }
+
+  public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
+    return isActive(info) && isPinnedShortcut(info) ?
+        ShortcutKey.fromItemInfo(info).getId() : null;
+  }
+
+  public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
+    return isActive(info) && isPinnedShortcut(info) ?
+        ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
+  }
+
+  private static boolean isActive(ItemInfo info) {
+    boolean isLoading = info instanceof WorkspaceItemInfo
+        && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+    return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+  }
+
+  private static boolean isApp(ItemInfo info) {
+    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+  }
+
+  private static boolean isPinnedShortcut(ItemInfo info) {
+    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+        && info.container != ItemInfo.NO_ID
+        && info instanceof WorkspaceItemInfo;
+  }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f2f2f3b..799762d 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,7 +20,7 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -41,7 +41,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.TouchController;
@@ -219,7 +218,7 @@
             // This can happen if something goes wrong during a state change/transition.
             AbstractFloatingView floatingView = (AbstractFloatingView) child;
             if (floatingView.isOpen()) {
-                postDelayed(() -> floatingView.close(false), SINGLE_FRAME_MS);
+                postDelayed(() -> floatingView.close(false), getSingleFrameMs(getContext()));
             }
         }
     }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index 1710aef..789bfd8 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
 
 package com.android.launcher3.model;
 
-import com.android.launcher3.AllAppsList;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 84a59b2..bd7dd86 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,10 +27,6 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -59,13 +55,6 @@
         mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
     }
 
-    public static boolean supportsShortcuts(ItemInfo info) {
-        boolean isItemPromise = info instanceof WorkspaceItemInfo
-                && ((WorkspaceItemInfo) info).hasPromiseIconUi();
-        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && !info.isDisabled() && !isItemPromise;
-    }
-
     /**
      * Queries for the shortcuts with the package name and provided ids.
      *
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index e9dc800..bd6ea50 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState() {
+    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
         return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
                 ContainerType.HOTSEAT : ContainerType.WORKSPACE;
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 5cc64dc..467ae02 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -17,9 +17,11 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Activity;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 
@@ -27,6 +29,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.util.TouchController;
 
@@ -95,4 +98,7 @@
 
     public static void clearSwipeSharedState(boolean finishAnimation) {}
 
+    public static Person[] getPersons(ShortcutInfo si) {
+        return Utilities.EMPTY_PERSON_ARRAY;
+    }
 }
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 61c7306..c6f55a7 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -20,6 +20,9 @@
 
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
 
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner"/>
 
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 8dc8cea..fc19baa 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -38,6 +38,7 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
@@ -330,30 +331,27 @@
     }
 
     protected void startAppFast(String packageName) {
-        final Instrumentation instrumentation = getInstrumentation();
-        final Intent intent = instrumentation.getContext().getPackageManager().
-                getLaunchIntentForPackage(packageName);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        instrumentation.getTargetContext().startActivity(intent);
-        assertTrue(packageName + " didn't start",
-                mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
+        startIntent(
+                getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
+                        packageName),
+                By.pkg(packageName).depth(0));
     }
 
     protected void startTestActivity(int activityNumber) {
         final String packageName = getAppPackageName();
-        final Instrumentation instrumentation = getInstrumentation();
-        final Intent intent = instrumentation.getContext().getPackageManager().
+        final Intent intent = getInstrumentation().getContext().getPackageManager().
                 getLaunchIntentForPackage(packageName);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setComponent(new ComponentName(packageName,
                 "com.android.launcher3.tests.Activity" + activityNumber));
-        instrumentation.getTargetContext().startActivity(intent);
-        assertTrue(packageName + " didn't start",
-                mDevice.wait(
-                        Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
-                        DEFAULT_UI_TIMEOUT));
+        startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
+    }
+
+    private void startIntent(Intent intent, BySelector selector) {
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        getInstrumentation().getTargetContext().startActivity(intent);
+        assertTrue("App didn't start: " + selector,
+                mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
     }
 
     public static String resolveSystemApp(String category) {
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4dab44f..0c87ab9 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -173,6 +173,7 @@
 
     @Test
     public void testWorkspace() throws Exception {
+        mLauncher.enableDebugTracing();
         final Workspace workspace = mLauncher.getWorkspace();
 
         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
@@ -190,7 +191,7 @@
                 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
                         isWorkspaceScrollable(launcher)));
         assertNotNull("ensureScrollable didn't add Chrome app",
-                workspace.tryGetWorkspaceAppIcon("Chrome"));
+                workspace.getWorkspaceAppIcon("Chrome"));
 
         // Test flinging workspace.
         workspace.flingBackward();
@@ -206,8 +207,9 @@
         assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
 
         // Test starting a workspace app.
-        final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
+        final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
         assertNotNull("No Chrome app in workspace", app);
+        mLauncher.disableDebugTracing();
     }
 
     public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -298,6 +300,7 @@
     @Test
     @PortraitLandscape
     public void testDragAppIcon() throws Throwable {
+        mLauncher.enableDebugTracing();
         // 1. Open all apps and wait for load complete.
         // 2. Drag icon to homescreen.
         // 3. Verify that the icon works on homescreen.
@@ -314,11 +317,13 @@
                 "Launcher activity is the top activity; expecting another activity to be the top "
                         + "one",
                 isInBackground(launcher)));
+        mLauncher.disableDebugTracing();
     }
 
     @Test
     @PortraitLandscape
     public void testDragShortcut() throws Throwable {
+        mLauncher.enableDebugTracing();
         // 1. Open all apps and wait for load complete.
         // 2. Find the app and long press it to show shortcuts.
         // 3. Press icon center until shortcuts appear
@@ -338,6 +343,7 @@
         } finally {
             allApps.unfreeze();
         }
+        mLauncher.disableDebugTracing();
     }
 
     public static String getAppPackageName() {
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 593cce8..899686b 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.util;
 
 import android.os.SystemClock;
+import android.util.Log;
 
 import org.junit.Assert;
 
@@ -16,7 +17,9 @@
     }
 
     public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
-        long endTime = SystemClock.uptimeMillis() + timeout;
+        final long startTime = SystemClock.uptimeMillis();
+        long endTime = startTime + timeout;
+        Log.d("Wait", "atMost: " + startTime + " - " + endTime);
         while (SystemClock.uptimeMillis() < endTime) {
             try {
                 if (condition.isTrue()) {
@@ -36,6 +39,7 @@
         } catch (Throwable t) {
             throw new RuntimeException(t);
         }
+        Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
         Assert.fail(message);
     }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 2042403..62fe26d 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -19,6 +19,7 @@
 import android.app.Application;
 import android.app.Application.ActivityLifecycleCallbacks;
 import android.os.Bundle;
+
 import androidx.test.InstrumentationRegistry;
 
 import com.android.launcher3.Launcher;
@@ -84,19 +85,27 @@
         }
 
         @Override
-        public void onActivityStarted(Activity activity) { }
+        public void onActivityStarted(Activity activity) {
+            if (activity instanceof Launcher) {
+                mActivity.getRotationHelper().forceAllowRotationForTesting(true);
+            }
+        }
 
         @Override
-        public void onActivityResumed(Activity activity) { }
+        public void onActivityResumed(Activity activity) {
+        }
 
         @Override
-        public void onActivityPaused(Activity activity) { }
+        public void onActivityPaused(Activity activity) {
+        }
 
         @Override
-        public void onActivityStopped(Activity activity) { }
+        public void onActivityStopped(Activity activity) {
+        }
 
         @Override
-        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+        }
 
         @Override
         public void onActivityDestroyed(Activity activity) {
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 9ff354a..f070280 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -120,7 +120,7 @@
             mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
                     hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
 
-            final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
+            final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
                     appIconSelector);
             return new AppIcon(mLauncher, appIcon);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index bbd2c29..25e6e8c 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -74,7 +74,7 @@
                 flingForward();
             }
 
-            mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
+            mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "dismissed all tasks")) {
                 return new Workspace(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f91e2ad..6348c41 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -178,6 +178,7 @@
         PackageManager pm = getContext().getPackageManager();
         ProviderInfo pi = pm.resolveContentProvider(
                 testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
+        assertNotNull("Cannot find content provider for " + testProviderAuthority, pi);
         ComponentName cn = new ComponentName(pi.packageName, pi.name);
 
         if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
@@ -331,8 +332,10 @@
 
         final String systemHealth = getSystemHealthMessage();
         if (systemHealth != null) {
-            message = message + ", which might be a consequence of system health problems:\n<<<\n"
-                    + systemHealth + "\n>>>";
+            message = message
+                    + ", which might be a consequence of system health "
+                    + "problems:\n<<<<<<<<<<<<<<<<<<\n"
+                    + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
         }
 
         log("Hierarchy dump for: " + message);
@@ -676,13 +679,6 @@
     }
 
     @NonNull
-    UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
-        final UiObject2 object = container.findObject(selector);
-        assertNotNull("Can't find an object with selector: " + selector, object);
-        return object;
-    }
-
-    @NonNull
     List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
         return container.findObjects(getLauncherObjectSelector(resName));
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index ebe5eac..a089a52 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -109,6 +109,7 @@
         DropBoxManager.Entry entry;
         StringBuilder errorDetails = new StringBuilder();
         while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
+            errorDetails.append("------------------------------\n");
             timestamp = entry.getTimeMillis();
             errorDetails.append(new Date(timestamp));
             errorDetails.append(": ");
@@ -135,7 +136,6 @@
                     "system_server_crash",
                     "system_server_native_crash",
                     "system_server_watchdog",
-                    "system_server_wtf",
             };
 
             for (String label : labels) {
@@ -143,7 +143,9 @@
                 if (crash != null) errors.append(crash);
             }
 
-            return errors.length() != 0 ? errors.toString() : null;
+            return errors.length() != 0
+                    ? "Current time: " + new Date(System.currentTimeMillis()) + "\n" + errors
+                    : null;
         } catch (Exception e) {
             return "Failed to get system health diags, maybe build your test via .bp instead of "
                     + ".mk? " + android.util.Log.getStackTraceString(e);
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 07f8b64..639902f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -108,10 +108,13 @@
      */
     @NonNull
     public AppIcon getWorkspaceAppIcon(String appName) {
-        return new AppIcon(mLauncher,
-                mLauncher.getObjectInContainer(
-                        verifyActiveContainer(),
-                        AppIcon.getAppIconSelector(appName, mLauncher)));
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get a workspace icon")) {
+            return new AppIcon(mLauncher,
+                    mLauncher.waitForObjectInContainer(
+                            verifyActiveContainer(),
+                            AppIcon.getAppIconSelector(appName, mLauncher)));
+        }
     }
 
     /**
@@ -142,13 +145,13 @@
 
     @NonNull
     public AppIcon getHotseatAppIcon(String appName) {
-        return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
+        return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
     }
 
     @NonNull
     public Folder getHotseatFolder(String appName) {
-        return new Folder(mLauncher, mLauncher.getObjectInContainer(
+        return new Folder(mLauncher, mLauncher.waitForObjectInContainer(
                 mHotseat, Folder.getSelector(appName, mLauncher)));
     }