Merge "Add distance threshold for assistant gesture fling" into ub-launcher3-qt-qpr1-dev
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 5318a12..8db875b 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,6 +43,7 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
 
 
     <!--
diff --git a/OWNERS b/OWNERS
index 6c1273f..538ca33 100644
--- a/OWNERS
+++ b/OWNERS
@@ -10,3 +10,6 @@
 sunnygoyal@google.com
 twickham@google.com
 winsonc@google.com
+
+per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
+per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d0cfcf9..212ce9b 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
+import android.content.Context;
 import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
@@ -115,10 +116,10 @@
     }
 
     public static float getDefaultSwipeHeight(Launcher launcher) {
-        return getDefaultSwipeHeight(launcher.getDeviceProfile());
+        return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
     }
 
-    public static float getDefaultSwipeHeight(DeviceProfile dp) {
+    public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
         return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
     }
 
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index d84633d..36d1c3e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -36,12 +36,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -57,8 +61,6 @@
 import java.util.Set;
 import java.util.function.Supplier;
 
-import androidx.annotation.NonNull;
-
 public abstract class BaseIconCache {
 
     private static final String TAG = "BaseIconCache";
@@ -84,6 +86,7 @@
 
     protected int mIconDpi;
     protected IconDB mIconDb;
+    protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
     protected String mSystemState = "";
 
     private final String mDbFileName;
@@ -227,12 +230,12 @@
 
     /**
      * Refreshes the system state definition used to check the validity of the cache. It
-     * incorporates all the properties that can affect the cache like locale and system-version.
+     * incorporates all the properties that can affect the cache like the list of enabled locale
+     * and system-version.
      */
     private void updateSystemState() {
-        final String locale =
-                mContext.getResources().getConfiguration().getLocales().toLanguageTags();
-        mSystemState = locale + "," + Build.VERSION.SDK_INT;
+        mLocaleList = mContext.getResources().getConfiguration().getLocales();
+        mSystemState = mLocaleList.toLanguageTags() + "," + Build.VERSION.SDK_INT;
     }
 
     protected String getIconSystemState(String packageName) {
@@ -269,7 +272,7 @@
         mCache.put(key, entry);
 
         ContentValues values = newContentValues(entry, entry.title.toString(),
-                componentName.getPackageName());
+                componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
         addIconToDB(values, componentName, info, userSerial);
     }
 
@@ -445,7 +448,7 @@
                     // Add the icon in the DB here, since these do not get written during
                     // package updates.
                     ContentValues values = newContentValues(
-                            iconInfo, entry.title.toString(), packageName);
+                            iconInfo, entry.title.toString(), packageName, null);
                     addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
 
                 } catch (NameNotFoundException e) {
@@ -504,23 +507,35 @@
         return false;
     }
 
-    static final class IconDB extends SQLiteCacheHelper {
-        private final static int RELEASE_VERSION = 26;
+    /**
+     * Returns a cursor for an arbitrary query to the cache db
+     */
+    public synchronized Cursor queryCacheDb(String[] columns, String selection,
+            String[] selectionArgs) {
+        return mIconDb.query(columns, selection, selectionArgs);
+    }
 
-        public final static String TABLE_NAME = "icons";
-        public final static String COLUMN_ROWID = "rowid";
-        public final static String COLUMN_COMPONENT = "componentName";
-        public final static String COLUMN_USER = "profileId";
-        public final static String COLUMN_LAST_UPDATED = "lastUpdated";
-        public final static String COLUMN_VERSION = "version";
-        public final static String COLUMN_ICON = "icon";
-        public final static String COLUMN_ICON_COLOR = "icon_color";
-        public final static String COLUMN_LABEL = "label";
-        public final static String COLUMN_SYSTEM_STATE = "system_state";
+    /**
+     * Cache class to store the actual entries on disk
+     */
+    public static final class IconDB extends SQLiteCacheHelper {
+        private static final int RELEASE_VERSION = 27;
 
-        public final static String[] COLUMNS_HIGH_RES = new String[] {
+        public static final String TABLE_NAME = "icons";
+        public static final String COLUMN_ROWID = "rowid";
+        public static final String COLUMN_COMPONENT = "componentName";
+        public static final String COLUMN_USER = "profileId";
+        public static final String COLUMN_LAST_UPDATED = "lastUpdated";
+        public static final String COLUMN_VERSION = "version";
+        public static final String COLUMN_ICON = "icon";
+        public static final String COLUMN_ICON_COLOR = "icon_color";
+        public static final String COLUMN_LABEL = "label";
+        public static final String COLUMN_SYSTEM_STATE = "system_state";
+        public static final String COLUMN_KEYWORDS = "keywords";
+
+        public static final String[] COLUMNS_HIGH_RES = new String[] {
                 IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
-        public final static String[] COLUMNS_LOW_RES = new String[] {
+        public static final String[] COLUMNS_LOW_RES = new String[] {
                 IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
 
         public IconDB(Context context, String dbFileName, int iconPixelSize) {
@@ -529,21 +544,23 @@
 
         @Override
         protected void onCreateTable(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
-                    COLUMN_COMPONENT + " TEXT NOT NULL, " +
-                    COLUMN_USER + " INTEGER NOT NULL, " +
-                    COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_ICON + " BLOB, " +
-                    COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_LABEL + " TEXT, " +
-                    COLUMN_SYSTEM_STATE + " TEXT, " +
-                    "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
-                    ");");
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+                    + COLUMN_COMPONENT + " TEXT NOT NULL, "
+                    + COLUMN_USER + " INTEGER NOT NULL, "
+                    + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_ICON + " BLOB, "
+                    + COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, "
+                    + COLUMN_LABEL + " TEXT, "
+                    + COLUMN_SYSTEM_STATE + " TEXT, "
+                    + COLUMN_KEYWORDS + " TEXT, "
+                    + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") "
+                    + ");");
         }
     }
 
-    private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName) {
+    private ContentValues newContentValues(BitmapInfo bitmapInfo, String label,
+            String packageName, @Nullable String keywords) {
         ContentValues values = new ContentValues();
         values.put(IconDB.COLUMN_ICON,
                 bitmapInfo.isLowRes() ? null : GraphicsUtils.flattenBitmap(bitmapInfo.icon));
@@ -551,7 +568,7 @@
 
         values.put(IconDB.COLUMN_LABEL, label);
         values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName));
-
+        values.put(IconDB.COLUMN_KEYWORDS, keywords);
         return values;
     }
 
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index addb51f..09f59b8 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -17,8 +17,11 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.LocaleList;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BitmapInfo;
 
 public interface CachingLogic<T> {
@@ -30,4 +33,12 @@
     CharSequence getLabel(T object);
 
     void loadIcon(Context context, T object, BitmapInfo target);
+
+    /**
+     * Provides a option list of keywords to associate with this object
+     */
+    @Nullable
+    default String getKeywords(T object, LocaleList localeList) {
+        return null;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index 8f1282d..24fc61b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -95,6 +95,10 @@
     private AppPredictor createPredictor(Client client, int count) {
         AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
 
+        if (apm == null) {
+          return null;
+        }
+
         AppPredictor predictor = apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(mContext)
                         .setUiSurface(client.id)
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 151ceb8..93d4de1 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
@@ -32,6 +32,7 @@
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
+import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
 
@@ -159,11 +160,15 @@
     }
 
     public static float getDefaultSwipeHeight(Launcher launcher) {
-        return getDefaultSwipeHeight(launcher.getDeviceProfile());
+        return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
     }
 
-    public static float getDefaultSwipeHeight(DeviceProfile dp) {
-        return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+    public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
+        float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+        if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
+            swipeHeight -= dp.getInsets().bottom;
+        }
+        return swipeHeight;
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 295585e..25cc33d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -40,7 +40,6 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.UserHandle;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -58,7 +57,6 @@
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.views.FloatingIconView;
@@ -202,9 +200,6 @@
         // This ensures then the next swipe up to all-apps starts from scroll 0.
         activity.getAppsView().reset(false /* animate */);
 
-        // Optimization, hide the all apps view to prevent layout while initializing
-        activity.getAppsView().getContentView().setVisibility(View.GONE);
-
         return new AnimationFactory() {
             private ShelfAnimState mShelfState;
             private boolean mIsAttachedToWindow;
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 a94f25d..14ff47b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -25,11 +25,13 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
 import com.android.quickstep.views.RecentsView;
@@ -162,6 +164,9 @@
 
         @Override
         public void run() {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "RecentsActivityCommand.run");
+            }
             long elapsedTime = mCreateTime - mLastToggleTime;
             mLastToggleTime = mCreateTime;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 4eb9df2..da46426 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -24,7 +24,7 @@
         switch (method) {
             case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
                 final float swipeHeight =
-                        OverviewState.getDefaultSwipeHeight(mDeviceProfile);
+                        OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile);
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                 return response;
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index b90f6c2..17457aa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
 import android.graphics.Matrix;
 import android.view.View;
 
@@ -47,8 +49,7 @@
     };
 
     public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
-                    c, R.string.task_overlay_factory_class));
+            forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
 
     public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
         final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
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 7a67a2a..0d29e5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -736,18 +736,19 @@
                                 : LAST_TASK;
             }
         } else {
-            if (mMode == Mode.NO_BUTTON && endVelocity < 0 && !mIsShelfPeeking) {
+            // If swiping at a diagonal, base end target on the faster velocity.
+            boolean isSwipeUp = endVelocity < 0;
+            boolean willGoToNewTaskOnSwipeUp =
+                    goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
+
+            if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+                endTarget = HOME;
+            } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) {
                 // If swiping at a diagonal, base end target on the faster velocity.
-                endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
-                        ? NEW_TASK : HOME;
-            } else if (endVelocity < 0) {
-                if (reachedOverviewThreshold) {
-                    endTarget = RECENTS;
-                } else {
-                    // If swiping at a diagonal, base end target on the faster velocity.
-                    endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
-                            ? NEW_TASK : RECENTS;
-                }
+                endTarget = NEW_TASK;
+            } else if (isSwipeUp) {
+                endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
+                        ? NEW_TASK : RECENTS;
             } else {
                 endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
             }
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 1069bed..1aa5365 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
@@ -29,6 +29,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -40,7 +41,9 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.views.IconLabelDotView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -62,7 +65,9 @@
 
     private final float mVelocity;
     private final float mSpringTransY;
-    private final View mViewToIgnore;
+
+    // The original view of the {@link FloatingIconView}.
+    private final View mOriginalView;
 
     private final List<Animator> mAnimators = new ArrayList<>();
 
@@ -72,9 +77,7 @@
     public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
             float velocity) {
         mVelocity = velocity;
-        // We ignore this view since it's visibility and position is controlled by
-        // the FloatingIconView.
-        mViewToIgnore = floatingViewOriginalView;
+        mOriginalView = floatingViewOriginalView;
 
         // Scale the translationY based on the initial velocity to better sync the workspace items
         // with the floating view.
@@ -86,16 +89,21 @@
         Workspace workspace = launcher.getWorkspace();
         CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
         ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
+        ViewGroup hotseat = launcher.getHotseat();
 
         boolean workspaceClipChildren = workspace.getClipChildren();
         boolean workspaceClipToPadding = workspace.getClipToPadding();
         boolean cellLayoutClipChildren = cellLayout.getClipChildren();
         boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
+        boolean hotseatClipChildren = hotseat.getClipChildren();
+        boolean hotseatClipToPadding = hotseat.getClipToPadding();
 
         workspace.setClipChildren(false);
         workspace.setClipToPadding(false);
         cellLayout.setClipChildren(false);
         cellLayout.setClipToPadding(false);
+        hotseat.setClipChildren(false);
+        hotseat.setClipToPadding(false);
 
         // Hotseat and QSB takes up two additional rows.
         int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
@@ -108,16 +116,18 @@
         }
 
         // Set up springs for the hotseat and qsb.
+        ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
         if (grid.isVerticalBarLayout()) {
-            ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0);
-            for (int i = hotseat.getChildCount() - 1; i >= 0; i--) {
-                View child = hotseat.getChildAt(i);
+            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
+                View child = hotseatChild.getChildAt(i);
                 CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
                 addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
             }
         } else {
-            View hotseat = launcher.getHotseat().getChildAt(0);
-            addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows);
+            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
+                View child = hotseatChild.getChildAt(i);
+                addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
+            }
 
             View qsb = launcher.findViewById(R.id.search_container_all_apps);
             addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
@@ -140,6 +150,8 @@
                 workspace.setClipToPadding(workspaceClipToPadding);
                 cellLayout.setClipChildren(cellLayoutClipChildren);
                 cellLayout.setClipToPadding(cellLayoutClipToPadding);
+                hotseat.setClipChildren(hotseatClipChildren);
+                hotseat.setClipToPadding(hotseatClipToPadding);
             }
         };
 
@@ -180,16 +192,35 @@
         springTransY.setStartDelay(startDelay);
         mAnimators.add(springTransY);
 
-        if (v == mViewToIgnore) {
-            return;
+        ObjectAnimator alpha = getAlphaAnimator(v, startDelay);
+        if (v == mOriginalView) {
+            // For IconLabelDotViews, we just want the label to fade in.
+            // Icon, badge, and dots will animate in separately (controlled via FloatingIconView)
+            if (v instanceof IconLabelDotView) {
+                alpha.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        IconLabelDotView view = (IconLabelDotView) v;
+                        view.setIconVisible(false);
+                        view.setForceHideDot(true);
+                    }
+                });
+            } else {
+                return;
+            }
         }
 
         v.setAlpha(0);
+        mAnimators.add(alpha);
+    }
+
+    private ObjectAnimator getAlphaAnimator(View v, long startDelay) {
         ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
         alpha.setInterpolator(LINEAR);
         alpha.setDuration(ALPHA_DURATION_MS);
         alpha.setStartDelay(startDelay);
-        mAnimators.add(alpha);
+        return alpha;
+
     }
 
     private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 9b157d1..1bf77f5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -97,6 +97,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.Themes;
@@ -1051,9 +1052,10 @@
         if (task != null) {
             ActivityManagerWrapper.getInstance().removeTask(task.key.id);
             if (shouldLog) {
+                ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
                 mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                        onEndListener.logAction, Direction.UP, index,
-                        TaskUtils.getLaunchComponentKeyForTask(task.key));
+                        onEndListener.logAction, Direction.UP, index, componentKey);
+                mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
new file mode 100644
index 0000000..e425088
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.provider.DeviceConfig;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+public class TogglableFlag extends BaseTogglableFlag {
+
+    public TogglableFlag(String key, boolean defaultValue, String description) {
+        super(key, defaultValue, description);
+    }
+
+    @Override
+    public boolean getInitialValue(boolean value) {
+        return DeviceConfig.getBoolean("launcher", getKey(), value);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 9f12484..dfab434 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,15 +16,12 @@
 package com.android.quickstep;
 
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
@@ -35,7 +32,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
@@ -52,7 +48,7 @@
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
-            new MainThreadInitializedObject<>(c -> new RecentsModel(c));
+            new MainThreadInitializedObject<>(RecentsModel::new);
 
     private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2f411ef..bf3cd8a 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,18 +16,19 @@
 
 package com.android.quickstep.logging;
 
-import android.content.Context;
-import android.content.Intent;
-import android.stats.launcher.nano.LauncherExtension;
-import android.stats.launcher.nano.LauncherTarget;
-
 import static android.stats.launcher.nano.Launcher.ALLAPPS;
 import static android.stats.launcher.nano.Launcher.HOME;
 import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
 import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
+import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
 import static android.stats.launcher.nano.Launcher.BACKGROUND;
 import static android.stats.launcher.nano.Launcher.OVERVIEW;
 
+import android.content.Context;
+import android.content.Intent;
+import android.stats.launcher.nano.Launcher;
+import android.stats.launcher.nano.LauncherExtension;
+import android.stats.launcher.nano.LauncherTarget;
 import android.view.View;
 
 import com.android.launcher3.ItemInfo;
@@ -38,8 +39,6 @@
 import com.android.systemui.shared.system.StatsLogCompat;
 import com.google.protobuf.nano.MessageNano;
 
-import androidx.annotation.Nullable;
-
 /**
  * This method calls the StatsLog hidden method until they are made available public.
  *
@@ -74,6 +73,27 @@
                 MessageNano.toByteArray(ext), true);
     }
 
+    @Override
+    public void logTaskDismiss(View v, ComponentKey componentKey) {
+        LauncherExtension ext = new LauncherExtension();
+        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
+        int srcState = OVERVIEW;
+        fillInLauncherExtension(v, ext);
+        StatsLogCompat.write(DISMISS_TASK, srcState, BACKGROUND /* dstState */,
+                MessageNano.toByteArray(ext), true);
+    }
+
+    @Override
+    public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
+        LauncherExtension ext = new LauncherExtension();
+        ext.srcTarget = new LauncherTarget[1];
+        int srcState = mStateProvider.getCurrentState();
+        fillInLauncherExtensionWithPageId(ext, pageId);
+        int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
+        StatsLogCompat.write(launcherAction, srcState, srcState,
+                MessageNano.toByteArray(ext), true);
+    }
+
     public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
         StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
         if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
@@ -88,6 +108,13 @@
         return true;
     }
 
+    public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
+        Target target = new Target();
+        target.pageIndex = pageId;
+        copy(target, ext.srcTarget[0]);
+        return true;
+    }
+
     private static void copy(Target src, LauncherTarget dst) {
         // fill in
     }
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 3747f9a..dc6b56e 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -156,12 +156,14 @@
                 mDragHandleProgress = 1;
                 mMidAlpha = 0;
             } else {
-                mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+                Context context = getContext();
+                mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
                 mMidProgress =  OVERVIEW.getVerticalProgress(mLauncher);
                 Rect hotseatPadding = dp.getHotseatLayoutPadding();
                 int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
                         - hotseatPadding.bottom - hotseatPadding.top;
-                float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
+                float dragHandleTop =
+                        Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
                 mDragHandleProgress =  1 - (dragHandleTop / mShiftRange);
             }
             mTopOffset = dp.getInsets().top - mShelfOffset;
diff --git a/quickstep/tests/OWNERS b/quickstep/tests/OWNERS
index 046d871..02e8ebc 100644
--- a/quickstep/tests/OWNERS
+++ b/quickstep/tests/OWNERS
@@ -1 +1,4 @@
 vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ec3d49a..a7c33a9 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -69,10 +69,9 @@
     private DigitalWellBeingToast getToast() {
         executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
         waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
-        waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
+        final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
 
         return getFromLauncher(launcher -> {
-            final TaskView task = getLatestTask(launcher);
             assertTrue("Latest task is not Calculator",
                     CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
             return task.getDigitalWellBeingToast();
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index f27f400..e295527 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -34,9 +34,9 @@
 
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.systemui.shared.system.QuickStepContract;
 
-import org.junit.Assert;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -79,6 +79,14 @@
                 description.getAnnotation(NavigationModeSwitch.class) != null) {
             Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
             return new Statement() {
+                private void assertTrue(String message, boolean condition) {
+                    if(!condition) {
+                        final AssertionError assertionError = new AssertionError(message);
+                        FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
+                        throw assertionError;
+                    }
+                }
+
                 @Override
                 public void evaluate() throws Throwable {
                     mLauncher.enableDebugTracing();
@@ -107,9 +115,9 @@
                         Log.e(TAG, "Exception", e);
                         throw e;
                     } finally {
-                        Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
+                        assertTrue("Couldn't set overlay",
+                                setActiveOverlay(prevOverlayPkg, originalMode));
                     }
-                    mLauncher.disableDebugTracing();
                 }
 
                 private void evaluateWithThreeButtons() throws Throwable {
@@ -176,7 +184,7 @@
                         latch.await(10, TimeUnit.SECONDS);
                         targetContext.getMainExecutor().execute(() ->
                                 sysUINavigationMode.removeModeChangeListener(listener));
-                        Assert.assertTrue("Navigation mode didn't change to " + expectedMode,
+                        assertTrue("Navigation mode didn't change to " + expectedMode,
                                 currentSysUiNavigationMode() == expectedMode);
                     }
 
@@ -184,7 +192,7 @@
                         if (mLauncher.getNavigationModel() == expectedMode) break;
                         Thread.sleep(100);
                     }
-                    Assert.assertTrue("Couldn't switch to " + overlayPackage,
+                    assertTrue("Couldn't switch to " + overlayPackage,
                             mLauncher.getNavigationModel() == expectedMode);
 
                     for (int i = 0; i != 100; ++i) {
@@ -192,7 +200,7 @@
                         Thread.sleep(100);
                     }
                     final String error = mLauncher.getNavigationModeMismatchError();
-                    Assert.assertTrue("Switching nav mode: " + error, error == null);
+                    assertTrue("Switching nav mode: " + error, error == null);
 
                     Thread.sleep(5000);
                     return true;
diff --git a/res/values/config.xml b/res/values/config.xml
index 638a411..0387184 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -72,6 +72,7 @@
     <string name="system_shortcut_factory_class" translatable="false"></string>
     <string name="app_launch_tracker_class" translatable="false"></string>
     <string name="test_information_handler_class" translatable="false"></string>
+    <string name="launcher_activity_logic_class" translatable="false"></string>
 
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
index 92bcc64..a3d1216 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
@@ -1,6 +1,8 @@
 package com.android.launcher3.config;
 
 
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -70,7 +72,7 @@
         };
     }
 
-    private void override(BaseFlags.TogglableFlag flag, boolean newValue) {
+    private void override(BaseTogglableFlag flag, boolean newValue) {
         if (!ruleInProgress) {
             throw new IllegalStateException(
                     "Rule isn't in progress. Did you remember to mark it with @Rule?");
@@ -93,7 +95,7 @@
 
     private void applyAnnotation(FlagOverride flagOverride) {
         boolean found = false;
-        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             if (flag.getKey().equals(flagOverride.key())) {
                 override(flag, flagOverride.value());
                 found = true;
@@ -109,7 +111,7 @@
      * Resets all flags to their default values.
      */
     private void clearOverrides() {
-        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             flag.setForTests(flag.getDefaultValue());
         }
     }
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e9324f9..42a4f5c 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -29,7 +29,8 @@
 
     private PackageInstallStateChangedTask newTask(String pkg, int progress) {
         int state = PackageInstallerCompat.STATUS_INSTALLING;
-        PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
+        PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress,
+                android.os.Process.myUserHandle());
         return new PackageInstallStateChangedTask(installInfo);
     }
 
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 733f295..8b49c06 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -89,7 +89,7 @@
     public void addPromiseApp(Context context,
                               PackageInstallerCompat.PackageInstallInfo installInfo) {
         ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
-                .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
+                .getApplicationInfo(installInfo.packageName, 0, installInfo.user);
         // only if not yet installed
         if (applicationInfo == null) {
             PromiseAppInfo info = new PromiseAppInfo(installInfo);
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index c84be4d..864fa6e 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,6 +22,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import androidx.recyclerview.widget.RecyclerView;
@@ -171,4 +172,13 @@
      * <p>Override in each subclass of this base class.
      */
     public void onFastScrollCompleted() {}
+
+    @Override
+    public void onScrollStateChanged(int state) {
+        super.onScrollStateChanged(state);
+
+        if (state == SCROLL_STATE_IDLE) {
+            AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 22c69f5..b113249 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -32,7 +32,6 @@
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
 import android.view.KeyEvent;
@@ -54,8 +53,8 @@
 import com.android.launcher3.icons.IconCache.IconLoadRequest;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.IconLabelDotView;
 
 import java.text.NumberFormat;
 
@@ -64,7 +63,8 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
+        IconLabelDotView {
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
@@ -413,7 +413,8 @@
         }
     }
 
-    public void forceHideDot(boolean forceHideDot) {
+    @Override
+    public void setForceHideDot(boolean forceHideDot) {
         if (mForceHideDot == forceHideDot) {
             return;
         }
@@ -602,6 +603,7 @@
         }
     }
 
+    @Override
     public void setIconVisible(boolean visible) {
         mIsIconVisible = visible;
         Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 7ab88a0..a90025e 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -142,8 +142,11 @@
 
     @Override
     public void setAlpha(int alpha) {
-        mAlpha = alpha;
-        mPaint.setAlpha(alpha);
+        if (mAlpha != alpha) {
+            mAlpha = alpha;
+            mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index e1ef954..0f006f7 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,16 +1,17 @@
 package com.android.launcher3;
 
-import android.content.Context;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
 
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 public class IconProvider implements ResourceBasedOverride {
 
-    public static IconProvider newInstance(Context context) {
-        return Overrides.getObject(IconProvider.class, context, R.string.icon_provider_class);
-    }
+    public static MainThreadInitializedObject<IconProvider> INSTANCE =
+            forOverride(IconProvider.class, R.string.icon_provider_class);
 
     public IconProvider() { }
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index e9b932a..670cd28 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -29,7 +30,6 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
 import android.os.Parcelable;
 import android.os.Process;
@@ -141,7 +141,8 @@
 
                         String pkg = getIntentPackage(info.launchIntent);
                         if (!TextUtils.isEmpty(pkg)
-                                && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
+                                && !launcherApps.isPackageEnabledForProfile(pkg, info.user)
+                                && !info.isActivity) {
                             if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
                                     + info.launchIntent);
                             continue;
@@ -250,7 +251,8 @@
     }
 
     public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
-        return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
+        return (WorkspaceItemInfo)
+                new PendingInstallShortcutInfo(info, context).getItemInfo().first;
     }
 
     public static void queueShortcut(ShortcutInfo info, Context context) {
@@ -261,8 +263,9 @@
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
     }
 
-    public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
+    public static void queueApplication(Intent data, UserHandle user, Context context) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
+                context);
     }
 
     public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
@@ -326,7 +329,7 @@
 
     private static class PendingInstallShortcutInfo {
 
-        final LauncherActivityInfo activityInfo;
+        final boolean isActivity;
         final ShortcutInfo shortcutInfo;
         final AppWidgetProviderInfo providerInfo;
 
@@ -340,7 +343,7 @@
          * Initializes a PendingInstallShortcutInfo received from a different app.
          */
         public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
-            activityInfo = null;
+            isActivity = false;
             shortcutInfo = null;
             providerInfo = null;
 
@@ -350,14 +353,13 @@
 
             launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
             label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
         }
 
         /**
          * Initializes a PendingInstallShortcutInfo to represent a launcher target.
          */
         public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
-            activityInfo = info;
+            isActivity = true;
             shortcutInfo = null;
             providerInfo = null;
 
@@ -372,8 +374,24 @@
         /**
          * Initializes a PendingInstallShortcutInfo to represent a launcher target.
          */
+        public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
+            isActivity = true;
+            shortcutInfo = null;
+            providerInfo = null;
+
+            this.data = data;
+            this.user = user;
+            mContext = context;
+
+            launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+            label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+        }
+
+        /**
+         * Initializes a PendingInstallShortcutInfo to represent a launcher target.
+         */
         public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
-            activityInfo = null;
+            isActivity = false;
             shortcutInfo = info;
             providerInfo = null;
 
@@ -390,7 +408,7 @@
          */
         public PendingInstallShortcutInfo(
                 AppWidgetProviderInfo info, int widgetId, Context context) {
-            activityInfo = null;
+            isActivity = false;
             shortcutInfo = null;
             providerInfo = info;
 
@@ -405,17 +423,7 @@
 
         public String encodeToString() {
             try {
-                if (activityInfo != null) {
-                    // If it a launcher target, we only need component name, and user to
-                    // recreate this.
-                    return new JSONStringer()
-                        .object()
-                        .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
-                        .key(APP_SHORTCUT_TYPE_KEY).value(true)
-                        .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
-                                .getSerialNumberForUser(user))
-                        .endObject().toString();
-                } else if (shortcutInfo != null) {
+                if (shortcutInfo != null) {
                     // If it a launcher target, we only need component name, and user to
                     // recreate this.
                     return new JSONStringer()
@@ -457,7 +465,8 @@
                 JSONStringer json = new JSONStringer()
                     .object()
                     .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
-                    .key(NAME_KEY).value(name);
+                    .key(NAME_KEY).value(name)
+                    .key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
                 if (icon != null) {
                     byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
                     json = json.key(ICON_KEY).value(
@@ -477,29 +486,18 @@
         }
 
         public Pair<ItemInfo, Object> getItemInfo() {
-            if (activityInfo != null) {
-                AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
-                final LauncherAppState app = LauncherAppState.getInstance(mContext);
-                // Set default values until proper values is loaded.
-                appInfo.title = "";
-                appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
-                final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
-                if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
-                    app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
-                } else {
-                    app.getModel().updateAndBindWorkspaceItem(() -> {
-                        app.getIconCache().getTitleAndIcon(
-                                si, activityInfo, false /* useLowResIcon */);
-                        return si;
-                    });
-                }
-                return Pair.create((ItemInfo) si, (Object) activityInfo);
+            if (isActivity) {
+                WorkspaceItemInfo si = createWorkspaceItemInfo(data,
+                        LauncherAppState.getInstance(mContext));
+                si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+                si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+                return Pair.create(si, null);
             } else if (shortcutInfo != null) {
-                WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
+                WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
                 LauncherIcons li = LauncherIcons.obtain(mContext);
-                si.applyFrom(li.createShortcutIcon(shortcutInfo));
+                itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
                 li.recycle();
-                return Pair.create((ItemInfo) si, (Object) shortcutInfo);
+                return Pair.create(itemInfo, shortcutInfo);
             } else if (providerInfo != null) {
                 LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
                         .fromProviderInfo(mContext, providerInfo);
@@ -511,15 +509,16 @@
                 widgetInfo.minSpanY = info.minSpanY;
                 widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
                 widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
-                return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
+                return Pair.create(widgetInfo, providerInfo);
             } else {
-                WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
-                return Pair.create((ItemInfo) si, null);
+                WorkspaceItemInfo itemInfo =
+                        createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
+                return Pair.create(itemInfo, null);
             }
         }
 
         public boolean isLauncherActivity() {
-            return activityInfo != null;
+            return isActivity;
         }
     }
 
@@ -534,7 +533,9 @@
             if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
                 LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
                         .resolveActivity(decoder.launcherIntent, decoder.user);
-                return info == null ? null : new PendingInstallShortcutInfo(info, context);
+                if (info != null) {
+                    return new PendingInstallShortcutInfo(info, context);
+                }
             } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
                 DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
                 List<ShortcutInfo> si = sm.queryForFullDetails(
@@ -578,7 +579,11 @@
                 data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
             }
 
-            return new PendingInstallShortcutInfo(data, decoder.user, context);
+            if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
+                return new PendingInstallShortcutInfo(data, context, decoder.user);
+            } else {
+                return new PendingInstallShortcutInfo(data, decoder.user, context);
+            }
         } catch (JSONException | URISyntaxException e) {
             Log.d(TAG, "Exception reading shortcut to add: " + e);
         }
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 134e116..3f723d1 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -22,6 +22,8 @@
 import android.os.Process;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.util.ContentWriter;
 
 /**
@@ -134,6 +136,7 @@
         return null;
     }
 
+    @Nullable
     public ComponentName getTargetComponent() {
         Intent intent = getIntent();
         if (intent != null) {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d07638a..2a801d6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -19,14 +19,12 @@
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
 import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
 
-import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
-import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -46,7 +44,7 @@
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
-            new MainThreadInitializedObject<>((c) -> new LauncherAppState(c));
+            new MainThreadInitializedObject<>(LauncherAppState::new);
 
     private final Context mContext;
     private final LauncherModel mModel;
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index d66e581..63914b0 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -227,6 +227,11 @@
 
     private void goToState(LauncherState state, boolean animated, long delay,
             final Runnable onCompleteRunnable) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
+                    state.getClass().getSimpleName() +
+                    " @ " + Log.getStackTraceString(new Throwable()));
+        }
         animated &= Utilities.areAnimationsEnabled(mLauncher);
         if (mLauncher.isInState(state)) {
             if (mConfig.mCurrentAnimation == null) {
@@ -407,6 +412,11 @@
             mState.onStateDisabled(mLauncher);
         }
         mState = state;
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " +
+                    state.getClass().getSimpleName() +
+                    " @ " + Log.getStackTraceString(new Throwable()));
+        }
         mState.onStateEnabled(mLauncher);
         mLauncher.onStateSet(mState);
 
@@ -426,6 +436,11 @@
         if (state != mCurrentStableState) {
             mLastStableState = state.getHistoryForState(mCurrentStableState);
             mCurrentStableState = state;
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
+                        state.getClass().getSimpleName() +
+                        " @ " + Log.getStackTraceString(new Throwable()));
+            }
         }
 
         state.onStateTransitionEnd(mLauncher);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index d2b8d4e..bbb3915 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1562,12 +1562,20 @@
         final boolean pagesFlipped = isPageOrderFlipped();
         info.setScrollable(getPageCount() > 1);
         if (getCurrentPage() < getPageCount() - 1) {
-            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
-                    : AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            info.addAction(pagesFlipped ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+            info.addAction(mIsRtl ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
         }
         if (getCurrentPage() > 0) {
-            info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
-                    : AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            info.addAction(pagesFlipped ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+            info.addAction(mIsRtl ?
+                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
+                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
         }
 
         // Accessibility-wise, PagedView doesn't support long click, so disabling it.
@@ -1607,8 +1615,21 @@
                 if (pagesFlipped ? scrollRight() : scrollLeft()) {
                     return true;
                 }
+            } break;
+            case android.R.id.accessibilityActionPageRight: {
+                if (!mIsRtl) {
+                  return scrollRight();
+                } else {
+                  return scrollLeft();
+                }
             }
-            break;
+            case android.R.id.accessibilityActionPageLeft: {
+                if (!mIsRtl) {
+                  return scrollLeft();
+                } else {
+                  return scrollRight();
+                }
+            }
         }
         return false;
     }
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index b0da6b9..b4078ee 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -18,28 +18,33 @@
 
 import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.Cursor;
+import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
-import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
 
 import java.util.List;
 
+import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
+
 /**
  * BroadcastReceiver to handle session commit intent.
  */
@@ -66,15 +71,29 @@
 
         SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
         UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+        PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context);
 
-        if (TextUtils.isEmpty(info.getAppPackageName()) ||
-                info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
+        if (TextUtils.isEmpty(info.getAppPackageName())
+                || info.getInstallReason() != PackageManager.INSTALL_REASON_USER
+                || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
+            packageInstallerCompat.removePromiseIconId(info.getSessionId());
             return;
         }
 
         queueAppIconAddition(context, info.getAppPackageName(), user);
     }
 
+    public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
+        String packageName = sessionInfo.getAppPackageName();
+        List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
+                .getActivityList(packageName, getUserHandle(sessionInfo));
+        if (activities == null || activities.isEmpty()) {
+            // Ensure application isn't already installed.
+            queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
+                    sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
+        }
+    }
+
     public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
         List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
                 .getActivityList(packageName, user);
@@ -82,7 +101,18 @@
             // no activity found
             return;
         }
-        InstallShortcutReceiver.queueActivityInfo(activities.get(0), context);
+        queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
+    }
+
+    private static void queueAppIconAddition(Context context, String packageName,
+            CharSequence label, Bitmap icon, UserHandle user) {
+        Intent data = new Intent();
+        data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
+                new ComponentName(packageName, "")).setPackage(packageName));
+        data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
+        data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+
+        InstallShortcutReceiver.queueApplication(data, user, context);
     }
 
     public static boolean isEnabled(Context context) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3bef598..ba122f9 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -732,7 +732,7 @@
         int[] array = new int[tokenizer.countTokens()];
         int count = 0;
         while (tokenizer.hasMoreTokens()) {
-            array[count] = Integer.parseInt(tokenizer.nextToken());
+            array[count] = Integer.parseInt(tokenizer.nextToken().trim());
             count++;
         }
         return array;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3be91d4..6612662 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1052,6 +1052,7 @@
             if (!mOverlayShown) {
                 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
+                mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
             }
             mOverlayShown = true;
             // Not announcing the overlay page for accessibility since it announces itself.
@@ -1061,6 +1062,7 @@
                 if (!ued.isPreviousHomeGesture()) {
                     mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
                         Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+                    mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
                 }
             } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
                 // When arriving to 0 overscroll from non-zero overscroll, announce page for
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index b72866c..050a8be 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -50,24 +50,26 @@
      * The icon was added as an auto-install app, and is not ready to be used. This flag can't
      * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
      * parsing.
+     *
+     * OR this icon was added due to it being an active install session created by the user.
      */
-    public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
+    public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
 
     /**
      * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
      * is set, then the icon is either being installed or is in a broken state.
      */
-    public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
+    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
 
     /**
      * Indicates that the widget restore has started.
      */
-    public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
+    public static final int FLAG_RESTORE_STARTED = 1 << 3;
 
     /**
      * Web UI supported.
      */
-    public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
+    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
 
     /**
      * The intent used to start the application.
@@ -210,7 +212,7 @@
     public ComponentName getTargetComponent() {
         ComponentName cn = super.getTargetComponent();
         if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
-                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
+                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) {
             // Legacy shortcuts and promise icons with web UI may not have a componentName but just
             // a packageName. In that case create a dummy componentName instead of adding additional
             // check everywhere.
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 4a2109e..293b867 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -628,20 +628,4 @@
 
         return super.performAccessibilityAction(action, arguments);
     }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        final boolean result = super.dispatchTouchEvent(ev);
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                if (result) mAllAppsStore.enableDeferUpdates(
-                        AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mAllAppsStore.disableDeferUpdates(AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
-                break;
-        }
-        return result;
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a0e9dc5..f82e380 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -26,13 +26,15 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -41,8 +43,6 @@
 
 import java.util.List;
 
-import androidx.recyclerview.widget.RecyclerView;
-
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
@@ -114,6 +114,13 @@
         if (mScrollbar != null) {
             mScrollbar.reattachThumbToScroll();
         }
+        if (getLayoutManager() instanceof AppsGridLayoutManager) {
+            AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
+            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
+                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+                return;
+            }
+        }
         scrollToPosition(0);
     }
 
@@ -420,13 +427,4 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
-
-    @Override
-    public void onScrollStateChanged(int state) {
-        super.onScrollStateChanged(state);
-
-        if (state == SCROLL_STATE_IDLE) {
-            AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
-        }
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 267363f..ca8dbeb 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -39,10 +39,8 @@
 
     // Defer updates flag used to defer all apps updates to the next draw.
     public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
-    // Defer updates flag used to defer all apps updates while the user interacts with all apps.
-    public static final int DEFER_UPDATES_USER_INTERACTION = 1 << 1;
     // Defer updates flag used to defer all apps updates by a test's request.
-    public static final int DEFER_UPDATES_TEST = 1 << 2;
+    public static final int DEFER_UPDATES_TEST = 1 << 1;
 
     private PackageUserKey mTempKey = new PackageUserKey(null, null);
     private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 5b3beec..3836c9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -18,7 +18,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
@@ -32,7 +31,6 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 1d19b53..1885d8f 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -31,11 +31,14 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import androidx.annotation.NonNull;
@@ -167,6 +170,10 @@
 
         @Override
         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.APP_NOT_DISABLED, "onPackagesSuspended: " +
+                        Arrays.toString(packageNames));
+            }
             mCallback.onPackagesSuspended(packageNames, user);
         }
 
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 4f4d641..11cb1f8 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,15 +19,23 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInstaller;
+import android.os.Process;
 import android.os.UserHandle;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.Utilities;
+
 public abstract class PackageInstallerCompat {
 
+    // Set<String> of session ids of promise icons that have been added to the home screen
+    // as FLAG_PROMISE_NEW_INSTALLS.
+    protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
+
     public static final int STATUS_INSTALLED = 0;
     public static final int STATUS_INSTALLING = 1;
     public static final int STATUS_FAILED = 2;
@@ -44,6 +52,10 @@
         }
     }
 
+    public static UserHandle getUserHandle(PackageInstaller.SessionInfo info) {
+        return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+    }
+
     /**
      * @return a map of active installs to their progress
      */
@@ -61,30 +73,44 @@
         public final String packageName;
         public final int state;
         public final int progress;
+        public final UserHandle user;
 
         private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
             this.state = STATUS_INSTALLING;
             this.packageName = info.getAppPackageName();
             this.componentName = new ComponentName(packageName, "");
             this.progress = (int) (info.getProgress() * 100f);
+            this.user = getUserHandle(info);
         }
 
-        public PackageInstallInfo(String packageName, int state, int progress) {
+        public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
             this.state = state;
             this.packageName = packageName;
             this.componentName = new ComponentName(packageName, "");
             this.progress = progress;
+            this.user = user;
         }
 
         public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
             return new PackageInstallInfo(info);
         }
 
-        public static PackageInstallInfo fromState(int state, String packageName) {
-            return new PackageInstallInfo(packageName, state, 0 /* progress */);
+        public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
+            return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
         }
 
     }
 
     public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
+
+    /**
+     * Returns true if a promise icon was already added to the home screen for {@param sessionId}.
+     * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
+     */
+    public abstract boolean promiseIconAddedForId(int sessionId);
+
+    /**
+     * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
+     */
+    public abstract void removePromiseIconId(int sessionId);
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 8a5eabc..bca66f7 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -21,17 +21,21 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionCallback;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.SparseArray;
 
+import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -39,11 +43,13 @@
 import java.util.Iterator;
 import java.util.List;
 
+import static com.android.launcher3.Utilities.getPrefs;
+
 public class PackageInstallerCompatVL extends PackageInstallerCompat {
 
     private static final boolean DEBUG = false;
 
-    @Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
+    @Thunk final SparseArray<PackageUserKey> mActiveSessions = new SparseArray<>();
 
     @Thunk final PackageInstaller mInstaller;
     private final IconCache mCache;
@@ -51,6 +57,7 @@
     private final Context mAppContext;
     private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
     private final LauncherAppsCompat mLauncherApps;
+    private final IntSet mPromiseIconIds;
 
     PackageInstallerCompatVL(Context context) {
         mAppContext = context.getApplicationContext();
@@ -59,17 +66,38 @@
         mWorker = new Handler(LauncherModel.getWorkerLooper());
         mInstaller.registerSessionCallback(mCallback, mWorker);
         mLauncherApps = LauncherAppsCompat.getInstance(context);
+        mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
+                getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
+
+        cleanUpPromiseIconIds();
+    }
+
+    private void cleanUpPromiseIconIds() {
+        IntArray existingIds = new IntArray();
+        for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
+            existingIds.add(info.getSessionId());
+        }
+        IntArray idsToRemove = new IntArray();
+
+        for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
+            if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
+                idsToRemove.add(mPromiseIconIds.getArray().get(i));
+            }
+        }
+        for (int i = idsToRemove.size() - 1; i >= 0; --i) {
+            mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
+        }
     }
 
     @Override
     public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
         HashMap<String, SessionInfo> activePackages = new HashMap<>();
-        UserHandle primaryUser = Process.myUserHandle();
         for (SessionInfo info : getAllVerifiedSessions()) {
-            addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
+            addSessionInfoToCache(info, getUserHandle(info));
             if (info.getAppPackageName() != null) {
                 activePackages.put(info.getAppPackageName(), info);
-                mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
+                mActiveSessions.put(info.getSessionId(),
+                        new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
             }
         }
         return activePackages;
@@ -78,7 +106,7 @@
     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())) {
+            if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
                 match = false;
             }
             if (match) {
@@ -108,6 +136,25 @@
         }
     }
 
+    /**
+     * Add a promise app icon to the workspace iff:
+     * - The settings for it are enabled
+     * - The user installed the app
+     * - There is a provided app icon (For apps with no launching activity, no icon is provided).
+     */
+    private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
+        if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+                && SessionCommitReceiver.isEnabled(mAppContext)
+                && sessionInfo != null
+                && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
+                && sessionInfo.getAppIcon() != null
+                && !mPromiseIconIds.contains(sessionInfo.getSessionId())) {
+            SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
+            mPromiseIconIds.add(sessionInfo.getSessionId());
+            updatePromiseIconPrefs();
+        }
+    }
+
     private final SessionCallback mCallback = new SessionCallback() {
 
         @Override
@@ -120,19 +167,29 @@
                             PackageInstallInfo.fromInstallingState(sessionInfo));
                 }
             }
+
+            tryQueuePromiseAppIcon(sessionInfo);
         }
 
         @Override
         public void onFinished(int sessionId, boolean success) {
             // For a finished session, we can't get the session info. So use the
             // packageName from our local cache.
-            String packageName = mActiveSessions.get(sessionId);
+            PackageUserKey key = mActiveSessions.get(sessionId);
             mActiveSessions.remove(sessionId);
 
-            if (packageName != null) {
-                sendUpdate(PackageInstallInfo.fromState(
-                        success ? STATUS_INSTALLED : STATUS_FAILED,
-                        packageName));
+            if (key != null && key.mPackageName != null) {
+                String packageName = key.mPackageName;
+                sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
+                        packageName, key.mUser));
+
+                if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
+                    LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+                    if (appState != null) {
+                        LauncherModel model = appState.getModel();
+                        model.onPackageRemoved(packageName, key.mUser);
+                    }
+                }
             }
         }
 
@@ -149,14 +206,18 @@
 
         @Override
         public void onBadgingChanged(int sessionId) {
-            pushSessionDisplayToLauncher(sessionId);
+            SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+            if (sessionInfo != null) {
+                tryQueuePromiseAppIcon(sessionInfo);
+            }
         }
 
         private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
             SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
             if (session != null && session.getAppPackageName() != null) {
-                mActiveSessions.put(sessionId, session.getAppPackageName());
-                addSessionInfoToCache(session, Process.myUserHandle());
+                mActiveSessions.put(session.getSessionId(),
+                        new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
+                addSessionInfoToCache(session, getUserHandle(session));
                 LauncherAppState app = LauncherAppState.getInstanceNoCreate();
                 if (app != null) {
                     app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
@@ -178,7 +239,7 @@
             if (!mSessionVerifiedMap.containsKey(pkg)) {
                 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
                 boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
-                        ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
+                        ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
                 mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
             }
         }
@@ -198,4 +259,23 @@
         }
         return list;
     }
+
+    @Override
+    public boolean promiseIconAddedForId(int sessionId) {
+        return mPromiseIconIds.contains(sessionId);
+    }
+
+    @Override
+    public void removePromiseIconId(int sessionId) {
+        if (mPromiseIconIds.contains(sessionId)) {
+            mPromiseIconIds.getArray().removeValue(sessionId);
+            updatePromiseIconPrefs();
+        }
+    }
+
+    private void updatePromiseIconPrefs() {
+        getPrefs(mAppContext).edit()
+                .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+                .apply();
+    }
 }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 54efcb7..ea6261a 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -18,20 +18,16 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.Keep;
-import androidx.annotation.VisibleForTesting;
 
+import androidx.annotation.VisibleForTesting;
 import com.android.launcher3.Utilities;
 
+import com.android.launcher3.uioverrides.TogglableFlag;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.SortedMap;
@@ -41,11 +37,9 @@
  * Defines a set of flags used to control various launcher behaviors.
  *
  * <p>All the flags should be defined here with appropriate default values.
- *
- * <p>This class is kept package-private to prevent direct access.
  */
 @Keep
-abstract class BaseFlags {
+public abstract class BaseFlags {
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
@@ -66,6 +60,11 @@
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
 
+    // When enabled a promise icon is added to the home screen when install session is active.
+    public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
+            new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
+                    "Adds a promise icon to the home screen for new install sessions.");
+
     // Enable moving the QSB on the 0th screen of the workspace
     public static final boolean QSB_ON_FIRST_SCREEN = true;
 
@@ -112,11 +111,15 @@
             "FAKE_LANDSCAPE_UI", false,
             "Rotate launcher UI instead of using transposed layout");
 
+    public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
+            "APP_SEARCH_IMPROVEMENTS", false,
+            "Adds localized title and keyword search and ranking");
+
     public static void initialize(Context context) {
         // Avoid the disk read for user builds
         if (Utilities.IS_DEBUG_DEVICE) {
             synchronized (sLock) {
-                for (TogglableFlag flag : sFlags) {
+                for (BaseTogglableFlag flag : sFlags) {
                     flag.initialize(context);
                 }
             }
@@ -132,27 +135,27 @@
         SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
         synchronized (sLock) {
             for (TogglableFlag flag : sFlags) {
-                flagsByKey.put(flag.key, flag);
+                flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag);
             }
         }
         return new ArrayList<>(flagsByKey.values());
     }
 
-    public static class TogglableFlag {
+    public static abstract class BaseTogglableFlag {
         private final String key;
         private final boolean defaultValue;
         private final String description;
         private boolean currentValue;
 
-        TogglableFlag(
+        public BaseTogglableFlag(
                 String key,
                 boolean defaultValue,
                 String description) {
             this.key = checkNotNull(key);
-            this.currentValue = this.defaultValue = defaultValue;
+            this.currentValue = this.defaultValue = getInitialValue(defaultValue);
             this.description = checkNotNull(description);
             synchronized (sLock) {
-                sFlags.add(this);
+                sFlags.add((TogglableFlag)this);
             }
         }
 
@@ -162,14 +165,16 @@
             currentValue = value;
         }
 
-        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
         public String getKey() {
             return key;
         }
+
         void initialize(Context context) {
             currentValue = getFromStorage(context, defaultValue);
         }
 
+        protected abstract boolean getInitialValue(boolean value);
+
         public void updateStorage(Context context, boolean value) {
             SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
                     Context.MODE_PRIVATE).edit();
@@ -213,7 +218,7 @@
                 return true;
             }
             if (o instanceof TogglableFlag) {
-                TogglableFlag that = (TogglableFlag) o;
+                BaseTogglableFlag that = (BaseTogglableFlag) o;
                 return (this.key.equals(that.getKey()))
                         && (this.defaultValue == that.getDefaultValue())
                         && (this.description.equals(that.getDescription()));
@@ -233,48 +238,4 @@
             return h$;
         }
     }
-
-    /**
-     * Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
-     * This is useful if we want to be able to control this flag from another process.
-     */
-    public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
-        private ContentResolver contentResolver;
-
-        ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
-            super(key, defaultValue, description);
-        }
-
-        @Override
-        public void initialize(Context context) {
-            contentResolver = context.getContentResolver();
-            contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
-                    new ContentObserver(new Handler(Looper.getMainLooper())) {
-                        @Override
-                        public void onChange(boolean selfChange) {
-                            superInitialize(context);
-                    }});
-            superInitialize(context);
-        }
-
-        private void superInitialize(Context context) {
-            super.initialize(context);
-        }
-
-        @Override
-        public void updateStorage(Context context, boolean value) {
-            if (contentResolver == null) {
-                return;
-            }
-            Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
-        }
-
-        @Override
-        boolean getFromStorage(Context context, boolean defaultValue) {
-            if (contentResolver == null) {
-                return defaultValue;
-            }
-            return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 5ecb186..54e5322 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -26,12 +26,13 @@
 import android.widget.Toast;
 
 import com.android.launcher3.R;
-import com.android.launcher3.config.BaseFlags.TogglableFlag;
 
 import androidx.preference.PreferenceDataStore;
 import androidx.preference.PreferenceFragment;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.SwitchPreference;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
 
 /**
  * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
@@ -62,7 +63,7 @@
 
         @Override
         public boolean getBoolean(String key, boolean defaultValue) {
-            for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
                 if (flag.getKey().equals(key)) {
                     return flag.getFromStorage(mContext, defaultValue);
                 }
@@ -83,7 +84,7 @@
         // flag with a different value than the default. That way, when we flip flags in
         // future, engineers will pick up the new value immediately. To accomplish this, we use a
         // custom preference data store.
-        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+        for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
             SwitchPreference switchPreference = new SwitchPreference(mContext);
             switchPreference.setKey(flag.getKey());
             switchPreference.setDefaultValue(flag.getDefaultValue());
@@ -99,7 +100,7 @@
     /**
      * Updates the summary to show the description and whether the flag overrides the default value.
      */
-    private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
+    private void updateSummary(SwitchPreference switchPreference, BaseTogglableFlag flag) {
         String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
         String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
         switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
@@ -134,7 +135,7 @@
         }
     }
 
-    private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
+    private boolean getFlagStateFromSharedPrefs(BaseTogglableFlag flag) {
         return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index bd2a03b..01e0f92 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -61,6 +61,9 @@
                 mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_CANCEL:
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_CANCEL");
+                }
                 mEventListener.onDriverDragCancel();
                 break;
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2ef6d70..f22b533 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -516,7 +516,7 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mFolderIcon.setBackgroundVisible(false);
+                mFolderIcon.setIconVisible(false);
                 mFolderIcon.drawLeaveBehindIfExists();
             }
             @Override
@@ -646,7 +646,7 @@
         clearFocus();
         if (mFolderIcon != null) {
             mFolderIcon.setVisibility(View.VISIBLE);
-            mFolderIcon.setBackgroundVisible(true);
+            mFolderIcon.setIconVisible(true);
             mFolderIcon.mFolderName.setTextVisibility(true);
             if (wasAnimated) {
                 mFolderIcon.animateBgShadowAndStroke();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 250169c..0e2d467 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -65,6 +65,7 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.IconLabelDotView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
@@ -73,7 +74,7 @@
 /**
  * An icon that can appear on in the workspace representing an {@link Folder}.
  */
-public class FolderIcon extends FrameLayout implements FolderListener {
+public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
 
     @Thunk Launcher mLauncher;
     @Thunk Folder mFolder;
@@ -107,6 +108,7 @@
 
     private Alarm mOpenAlarm = new Alarm();
 
+    private boolean mForceHideDot;
     @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
     private FolderDotInfo mDotInfo = new FolderDotInfo();
     private DotRenderer mDotRenderer;
@@ -409,6 +411,20 @@
         return mPreviewLayoutRule;
     }
 
+    @Override
+    public void setForceHideDot(boolean forceHideDot) {
+        if (mForceHideDot == forceHideDot) {
+            return;
+        }
+        mForceHideDot = forceHideDot;
+
+        if (forceHideDot) {
+            invalidate();
+        } else if (hasDot()) {
+            animateDotScale(0, 1);
+        }
+    }
+
     /**
      * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
      * (the dot is being added or removed).
@@ -468,7 +484,8 @@
         mBackground.setInvalidateDelegate(this);
     }
 
-    public void setBackgroundVisible(boolean visible) {
+    @Override
+    public void setIconVisible(boolean visible) {
         mBackgroundIsVisible = visible;
         invalidate();
     }
@@ -509,7 +526,7 @@
     }
 
     public void drawDot(Canvas canvas) {
-        if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
+        if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
             Rect iconBounds = mDotParams.iconBounds;
             BubbleTextView.getIconBounds(this, iconBounds,
                     mLauncher.getWallpaperDeviceProfile().iconSizePx);
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index c9566cb..288749f 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.graphics;
 
 import static com.android.launcher3.graphics.IconShape.getShapePath;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -31,6 +32,8 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.R;
@@ -38,16 +41,13 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import androidx.annotation.UiThread;
-
 /**
  * Factory for creating new drawables.
  */
 public class DrawableFactory implements ResourceBasedOverride {
 
     public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
-                        c.getApplicationContext(), R.string.drawable_factory_class));
+            forOverride(DrawableFactory.class, R.string.drawable_factory_class);
 
     protected final UserHandle mMyUser = Process.myUserHandle();
     protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 648445e..55d58b9 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -29,6 +29,8 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconProvider;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -36,8 +38,8 @@
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.WorkspaceItemInfo;
 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.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -50,8 +52,6 @@
 
 import java.util.function.Supplier;
 
-import androidx.annotation.NonNull;
-
 /**
  * Cache of application icons.  Icons can be made from any thread.
  */
@@ -75,11 +75,11 @@
         super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
                 inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
         mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
-        mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this);
+        mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mUserManager = UserManagerCompat.getInstance(mContext);
         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconProvider = IconProvider.newInstance(context);
+        mIconProvider = IconProvider.INSTANCE.get(context);
     }
 
     @Override
diff --git a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
similarity index 67%
rename from src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
rename to src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 7c99633..f9a94da 100644
--- a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,14 +20,23 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
+import com.android.launcher3.IconProvider;
+import com.android.launcher3.R;
 import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.util.ResourceBasedOverride;
 
-public class LauncherActivtiyCachingLogic implements CachingLogic<LauncherActivityInfo> {
+/**
+ * Caching logic for LauncherActivityInfo.
+ */
+public class LauncherActivityCachingLogic
+        implements CachingLogic<LauncherActivityInfo>, ResourceBasedOverride {
 
-    private final IconCache mCache;
-
-    public LauncherActivtiyCachingLogic(IconCache cache) {
-        mCache = cache;
+    /**
+     * Creates and returns a new instance
+     */
+    public static LauncherActivityCachingLogic newInstance(Context context) {
+        return Overrides.getObject(LauncherActivityCachingLogic.class, context,
+                R.string.launcher_activity_logic_class);
     }
 
     @Override
@@ -49,8 +58,10 @@
     public void loadIcon(Context context, LauncherActivityInfo object,
             BitmapInfo target) {
         LauncherIcons li = LauncherIcons.obtain(context);
-        li.createBadgedIconBitmap(mCache.getFullResIcon(object),
+        li.createBadgedIconBitmap(
+                IconProvider.INSTANCE.get(context)
+                        .getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
                 object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
         li.recycle();
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 9b9543e..cad95b0 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -40,5 +40,7 @@
 
     public void logAppLaunch(View v, Intent intent) { }
     public void logTaskLaunch(View v, ComponentKey key) { }
+    public void logTaskDismiss(View v, ComponentKey key) { }
+    public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) { }
     public void verify() {}     // TODO: should move into robo tests
 }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ed0d470..7d4f2f7 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -16,6 +16,8 @@
 package com.android.launcher3.model;
 
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.UserHandle;
 import android.util.LongSparseArray;
 import android.util.Pair;
@@ -32,6 +34,8 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 
@@ -85,6 +89,10 @@
                 }
             }
 
+            PackageInstallerCompat packageInstaller =
+                    PackageInstallerCompat.getInstance(app.getContext());
+            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext());
+
             for (ItemInfo item : filteredItems) {
                 // Find appropriate space for the item.
                 int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
@@ -101,6 +109,36 @@
                     throw new RuntimeException("Unexpected info type");
                 }
 
+                if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
+                    WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
+                    String packageName = item.getTargetComponent() != null
+                            ? item.getTargetComponent().getPackageName() : null;
+                    if (packageName == null) {
+                        continue;
+                    }
+                    SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
+                            packageName);
+                    if (sessionInfo == null) {
+                        List<LauncherActivityInfo> activities = launcherApps
+                                .getActivityList(packageName, item.user);
+                        if (activities != null && !activities.isEmpty()) {
+                            // App was installed while launcher was in the background.
+                            itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
+                                    .makeWorkspaceItem();
+                            WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
+                            wii.title = "";
+                            wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
+                            app.getIconCache().getTitleAndIcon(wii,
+                                    ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
+                        } else {
+                            // Session was cancelled, do not add.
+                            continue;
+                        }
+                    } else {
+                        workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+                    }
+                }
+
                 // Add the shortcut to the db
                 getModelWriter().addItemToDatabase(itemInfo,
                         LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 1613d47..29a46cf 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -15,18 +15,18 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 
 import android.content.ComponentName;
 import android.os.UserHandle;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import androidx.annotation.Nullable;
-
 /**
  * Callback for receiving various app launch events
  */
@@ -43,8 +43,7 @@
 
 
     public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
-            new MainThreadInitializedObject<>(c ->
-                    getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
+            forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
 
     public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
             @Nullable String container) { }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1a03b77..1c39d1f 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -227,7 +227,7 @@
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
             }
-        } else if  (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
+        } else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
             if (TextUtils.isEmpty(info.title)) {
                 info.title = getTitle();
             }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 0138572..4b01b5e 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -49,8 +49,8 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
@@ -61,7 +61,7 @@
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
+import com.android.launcher3.icons.LauncherActivityCachingLogic;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
@@ -196,7 +196,7 @@
             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
             setIgnorePackages(updateHandler);
             updateHandler.updateIcons(allActivityList,
-                    new LauncherActivtiyCachingLogic(mApp.getIconCache()),
+                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                     mApp.getModel()::onPackageIconsUpdated);
 
             // Take a break
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5f6d128..9fcab38 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.os.Process;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
@@ -56,7 +55,7 @@
                 ApplicationInfo ai = app.getContext()
                         .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
                 if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
-                    app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
+                    app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 // Ignore
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c37ed99..4428c8e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -55,6 +56,8 @@
 import java.util.HashSet;
 import java.util.List;
 
+import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+
 /**
  * Handles updates due to changes in package manager (app installed/updated/removed)
  * or when a user availability changes.
@@ -85,6 +88,10 @@
 
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.APP_NOT_DISABLED, "PackageUpdatedTask: " + mOp + ", " +
+                    Arrays.toString(mPackages));
+        }
         final Context context = app.getContext();
         final IconCache iconCache = app.getIconCache();
 
@@ -99,7 +106,7 @@
                     if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
                     iconCache.updateIconsForPkg(packages[i], mUser);
                     if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
-                        appsList.removePackage(packages[i], Process.myUserHandle());
+                        appsList.removePackage(packages[i], mUser);
                     }
                     appsList.addPackage(context, packages[i], mUser);
 
@@ -227,8 +234,7 @@
                                     isTargetValid = LauncherAppsCompat.getInstance(context)
                                             .isActivityEnabledForProfile(cn, mUser);
                                 }
-                                if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
-                                        && !isTargetValid) {
+                                if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) {
                                     if (updateWorkspaceItemIntent(context, si, packageName)) {
                                         infoUpdated = true;
                                     } else if (si.hasPromiseIconUi()) {
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index bfa4ba9..a1917ec 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -20,16 +20,14 @@
 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;
 
+import com.android.launcher3.Utilities;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * The key data associated with the notification, used to determine what to include
  * in dots and dummy popup views before they are populated.
@@ -39,8 +37,9 @@
 public class NotificationKeyData {
     public final String notificationKey;
     public final String shortcutId;
+    @NonNull
+    public final String[] personKeysFromNotification;
     public int count;
-    @NonNull public final String[] personKeysFromNotification;
 
     private NotificationKeyData(String notificationKey, String shortcutId, int count,
             String[] personKeysFromNotification) {
@@ -70,7 +69,8 @@
         if (people == null || people.isEmpty()) {
             return Utilities.EMPTY_STRING_ARRAY;
         }
-        return people.stream().map(Person::getKey).sorted().toArray(String[]::new);
+        return people.stream().filter(person -> person.getKey() != null)
+                .map(Person::getKey).sorted().toArray(String[]::new);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index baaad65..15fb4ce 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -36,7 +36,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
@@ -53,7 +52,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
 import com.android.launcher3.dot.DotInfo;
@@ -65,10 +63,8 @@
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
@@ -301,7 +297,7 @@
         }
 
         mLauncher.getDragController().addDragListener(this);
-        mOriginalIcon.forceHideDot(true);
+        mOriginalIcon.setForceHideDot(true);
 
         // All views are added. Animate layout from now on.
         setLayoutTransition(new LayoutTransition());
@@ -564,14 +560,14 @@
     protected void onCreateCloseAnimation(AnimatorSet anim) {
         // Animate original icon's text back in.
         anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
-        mOriginalIcon.forceHideDot(false);
+        mOriginalIcon.setForceHideDot(false);
     }
 
     @Override
     protected void closeComplete() {
         super.closeComplete();
         mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
-        mOriginalIcon.forceHideDot(false);
+        mOriginalIcon.setForceHideDot(false);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 563f3b3..78bd81b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -131,6 +131,7 @@
         @Override
         public View.OnClickListener getOnClickListener(final Launcher launcher,
                 final ItemInfo itemInfo) {
+            if (itemInfo.getTargetComponent() == null) return null;
             final List<WidgetItem> widgets =
                     launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
                             itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
index 516fafa..37a2092 100644
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -24,13 +28,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-
 public class SystemShortcutFactory implements ResourceBasedOverride {
 
     public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
-            new MainThreadInitializedObject<>(c -> Overrides.getObject(
-                    SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class));
+            forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
 
     /** Note that these are in order of priority. */
     private final SystemShortcut[] mAllShortcuts;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 3c0c5fd..d643a0b 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -173,12 +173,6 @@
         values.put(Favorites.PROFILE_ID, newProfileId);
         db.update(Favorites.TABLE_NAME, values, "profileId = ?",
                 new String[]{Long.toString(oldProfileId)});
-
-        // Change default value of the column.
-        db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
-        Favorites.addTableToDb(db, newProfileId, false);
-        db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
-        dropTable(db, "favorites_old");
     }
 
 
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index bab454f..243ff6f 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import android.os.Debug;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -112,7 +113,14 @@
                 }
                 break;
             }
+
+            case TestProtocol.REQUEST_TOTAL_PSS_KB: {
+                Debug.MemoryInfo mem = new Debug.MemoryInfo();
+                Debug.getMemoryInfo(mem);
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
+                break;
+            }
         }
         return response;
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index cb8f811..60a59dd 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -73,6 +73,7 @@
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
     public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
+    public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
 
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
@@ -80,4 +81,7 @@
 
     public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
     public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
+    public static final String APP_NOT_DISABLED = "b/139891609";
+    public static final String ALL_APPS_UPON_RECENTS = "b/139941530";
+    public static final String STABLE_STATE_MISMATCH = "b/140311911";
 }
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 2ee0328..e185a31 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -18,12 +18,13 @@
 import android.content.Context;
 import android.os.Looper;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 
 import java.util.concurrent.ExecutionException;
 
-import androidx.annotation.VisibleForTesting;
-
 /**
  * Utility class for defining singletons which are initiated on main thread.
  */
@@ -60,6 +61,14 @@
         mValue = value;
     }
 
+    /**
+     * Initializes a provider based on resource overrides
+     */
+    public static <T extends ResourceBasedOverride> MainThreadInitializedObject<T> forOverride(
+            Class<T> clazz, int resourceId) {
+        return new MainThreadInitializedObject<>(c -> Overrides.getObject(clazz, c, resourceId));
+    }
+
     public interface ObjectProvider<T> {
 
         T get(Context context);
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index e624517..8dc45f5 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -3,6 +3,8 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 
@@ -15,7 +17,9 @@
     public UserHandle mUser;
     private int mHashCode;
 
+    @Nullable
     public static PackageUserKey fromItemInfo(ItemInfo info) {
+        if (info.getTargetComponent() == null) return null;
         return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user);
     }
 
@@ -38,6 +42,7 @@
      * @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
      */
     public boolean updateFromItemInfo(ItemInfo info) {
+        if (info.getTargetComponent() == null) return false;
         if (ShortcutUtil.supportsShortcuts(info)) {
             update(info.getTargetComponent().getPackageName(), info.user);
             return true;
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f948beb..a4518ba 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -153,15 +153,15 @@
     }
 
     protected void handleClose(boolean animate, long defaultDuration) {
-        if (mIsOpen && !animate) {
+        if (!mIsOpen) {
+            return;
+        }
+        if (!animate) {
             mOpenCloseAnimator.cancel();
             setTranslationShift(TRANSLATION_SHIFT_CLOSED);
             onCloseComplete();
             return;
         }
-        if (!mIsOpen) {
-            return;
-        }
         mOpenCloseAnimator.setValues(
                 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
         mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 799762d..c08b659 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -29,6 +29,7 @@
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
@@ -41,6 +42,7 @@
 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;
@@ -261,6 +263,10 @@
             }
             case ACTION_CANCEL:
             case ACTION_UP:
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE,
+                            "BaseDragLayer.ACTION_UP/CANCEL " + ev);
+                }
                 mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
                 mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
                 break;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index e09a9e8..15b8d46 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -18,7 +18,6 @@
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.Utilities.getFullDrawable;
-import static com.android.launcher3.Utilities.isRtl;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -564,11 +563,6 @@
      */
     private void checkIconResult(View originalView, boolean isOpening) {
         CancellationSignal cancellationSignal = new CancellationSignal();
-        if (!isOpening) {
-            // Hide immediately since the floating view starts at a different location.
-            originalView.setVisibility(INVISIBLE);
-            cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE));
-        }
 
         if (mIconLoadResult == null) {
             Log.w(TAG, "No icon load result found in checkIconResult");
@@ -580,7 +574,7 @@
                 setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
                         mIconLoadResult.iconOffset);
                 if (isOpening) {
-                    originalView.setVisibility(INVISIBLE);
+                    hideOriginalView(originalView);
                 }
             } else {
                 mIconLoadResult.onIconLoaded = () -> {
@@ -591,15 +585,26 @@
                     setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
                             mIconLoadResult.iconOffset);
 
-                    // Delay swapping views until the icon is loaded to prevent a flash.
                     setVisibility(VISIBLE);
-                    originalView.setVisibility(INVISIBLE);
+                    if (isOpening) {
+                        // Delay swapping views until the icon is loaded to prevent a flash.
+                        hideOriginalView(originalView);
+                    }
                 };
                 mLoadIconSignal = cancellationSignal;
             }
         }
     }
 
+    private void hideOriginalView(View originalView) {
+        if (originalView instanceof BubbleTextView) {
+            ((BubbleTextView) originalView).setIconVisible(false);
+            ((BubbleTextView) originalView).setForceHideDot(true);
+        } else {
+            originalView.setVisibility(INVISIBLE);
+        }
+    }
+
     private void setBackgroundDrawableBounds(float scale) {
         sTmpRect.set(mFinalDrawableBounds);
         Utilities.scaleRectAboutCenter(sTmpRect, scale);
@@ -776,7 +781,12 @@
 
             if (hideOriginal) {
                 if (isOpening) {
-                    originalView.setVisibility(VISIBLE);
+                    if (originalView instanceof BubbleTextView) {
+                        ((BubbleTextView) originalView).setIconVisible(true);
+                        ((BubbleTextView) originalView).setForceHideDot(false);
+                    } else {
+                        originalView.setVisibility(VISIBLE);
+                    }
                     view.finish(dragLayer);
                 } else {
                     view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
@@ -804,38 +814,33 @@
             }
         });
 
-        if (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) {
+        if (mBadge != null) {
             ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
             badgeFade.addUpdateListener(valueAnimator -> invalidate());
             fade.play(badgeFade);
         }
 
-        if (originalView instanceof BubbleTextView) {
-            BubbleTextView btv = (BubbleTextView) originalView;
-            btv.forceHideDot(true);
+        if (originalView instanceof IconLabelDotView) {
+            IconLabelDotView view = (IconLabelDotView) originalView;
             fade.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    btv.forceHideDot(false);
+                    view.setIconVisible(true);
+                    view.setForceHideDot(false);
                 }
             });
         }
 
-        if (originalView instanceof FolderIcon) {
-            FolderIcon folderIcon = (FolderIcon) originalView;
-            folderIcon.setBackgroundVisible(false);
-            folderIcon.getFolderName().setTextVisibility(false);
-            fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
+        if (originalView instanceof BubbleTextView) {
+            BubbleTextView btv = (BubbleTextView) originalView;
             fade.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationEnd(Animator animation) {
-                    folderIcon.setBackgroundVisible(true);
-                    if (folderIcon.hasDot()) {
-                        folderIcon.animateDotScale(0, 1f);
-                    }
+                public void onAnimationStart(Animator animation) {
+                    btv.setIconVisible(true);
                 }
             });
-        } else {
+            fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
+        } else if (!(originalView instanceof FolderIcon)) {
             fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
         }
 
diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java
new file mode 100644
index 0000000..057caaf
--- /dev/null
+++ b/src/com/android/launcher3/views/IconLabelDotView.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+/**
+ * A view that has an icon, label, and notification dot.
+ */
+public interface IconLabelDotView {
+    void setIconVisible(boolean visible);
+    void setForceHideDot(boolean hide);
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 63f7427..465df44 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -18,10 +18,8 @@
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.text.TextUtils;
@@ -33,6 +31,9 @@
 import android.view.View.OnLongClickListener;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -46,7 +47,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.VisibleForTesting;
 
 /**
  * Popup shown on long pressing an empty space in launcher
@@ -169,16 +169,17 @@
     }
 
     public static boolean onWidgetsClicked(View view) {
-        return openWidgets(Launcher.getLauncher(view.getContext()));
+        return openWidgets(Launcher.getLauncher(view.getContext())) != null;
     }
 
-    public static boolean openWidgets(Launcher launcher) {
+    /** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */
+    @Nullable
+    public static WidgetsFullSheet openWidgets(Launcher launcher) {
         if (launcher.getPackageManager().isSafeMode()) {
             Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
-            return false;
+            return null;
         } else {
-            WidgetsFullSheet.show(launcher, true /* animated */);
-            return true;
+            return WidgetsFullSheet.show(launcher, true /* animated */);
         }
     }
 
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index c360117..da1df3f 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,14 +18,14 @@
 import static android.content.Context.ACCESSIBILITY_SERVICE;
 import static android.view.MotionEvent.ACTION_DOWN;
 
+import static androidx.core.graphics.ColorUtils.compositeColors;
+
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
-import static androidx.core.graphics.ColorUtils.compositeColors;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.Keyframe;
@@ -47,6 +47,13 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
@@ -62,15 +69,10 @@
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.WidgetsFullSheet;
 
 import java.util.List;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
 
 /**
  * Simple scrim which draws a flat color
@@ -325,7 +327,7 @@
 
         if (enabled) {
             stateManager.addStateListener(this);
-            handleStateChangedComplete(mLauncher.getStateManager().getState());
+            handleStateChangedComplete(stateManager.getState());
         } else {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
         }
@@ -437,7 +439,24 @@
             } else if (action == WALLPAPERS) {
                 return OptionsPopupView.startWallpaperPicker(ScrimView.this);
             } else if (action == WIDGETS) {
-                return OptionsPopupView.onWidgetsClicked(ScrimView.this);
+                int originalImportanceForAccessibility = getImportantForAccessibility();
+                setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+                WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
+                if (widgetsFullSheet == null) {
+                    setImportantForAccessibility(originalImportanceForAccessibility);
+                    return false;
+                }
+                widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View view) {}
+
+                    @Override
+                    public void onViewDetachedFromWindow(View view) {
+                        setImportantForAccessibility(originalImportanceForAccessibility);
+                        widgetsFullSheet.removeOnAttachStateChangeListener(this);
+                    }
+                });
+                return true;
             } else if (action == SETTINGS) {
                 return OptionsPopupView.startSettings(ScrimView.this);
             }
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 7119aea..09b1890 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,6 +27,8 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -78,8 +80,9 @@
      * Gets all the manifest and dynamic shortcuts associated with the given package and user,
      * to be displayed in the shortcuts container on long press.
      */
-    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+    public List<ShortcutInfo> queryForShortcutsContainer(@Nullable ComponentName activity,
             UserHandle user) {
+        if (activity == null) return Collections.EMPTY_LIST;
         return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
                 activity.getPackageName(), activity, null, user);
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
new file mode 100644
index 0000000..e875a3c
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -0,0 +1,31 @@
+/*
+ * 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.uioverrides;
+
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+public class TogglableFlag extends BaseTogglableFlag {
+
+    public TogglableFlag(String key, boolean defaultValue, String description) {
+        super(key, defaultValue, description);
+    }
+
+    @Override
+    public boolean getInitialValue(boolean value) {
+        return value;
+    }
+}
diff --git a/tests/OWNERS b/tests/OWNERS
index 046d871..02e8ebc 100644
--- a/tests/OWNERS
+++ b/tests/OWNERS
@@ -1 +1,4 @@
 vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index fc19baa..dc890bb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -24,7 +24,6 @@
 
 import static java.lang.System.exit;
 
-import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -112,6 +111,7 @@
                             launcher ->
                                     checkLauncherIntegrity(launcher, containerType)));
         }
+        mLauncher.enableDebugTracing();
     }
 
     protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -187,14 +187,6 @@
         }
     }
 
-    protected void lockRotation(boolean naturalOrientation) throws RemoteException {
-        if (naturalOrientation) {
-            mDevice.setOrientationNatural();
-        } else {
-            mDevice.setOrientationRight();
-        }
-    }
-
     protected void clearLauncherData() throws IOException, InterruptedException {
         if (TestHelpers.isInLauncherProcess()) {
             LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
@@ -202,6 +194,7 @@
             resetLoaderState();
         } else {
             clearPackageData(mDevice.getLauncherPackageName());
+            mLauncher.enableDebugTracing();
         }
     }
 
@@ -275,6 +268,12 @@
 
     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
     // flakiness.
+    protected <T> T getOnceNotNull(String message, Function<Launcher, T> f) {
+        return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
+    }
+
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
     protected void waitForLauncherCondition(
             String message, Function<Launcher, Boolean> condition, long timeout) {
         if (!TestHelpers.isInLauncherProcess()) return;
@@ -283,6 +282,20 @@
 
     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
     // flakiness.
+    protected <T> T getOnceNotNull(String message, Function<Launcher, T> f, long timeout) {
+        if (!TestHelpers.isInLauncherProcess()) return null;
+
+        final Object[] output = new Object[1];
+        Wait.atMost(message, () -> {
+            final Object fromLauncher = getFromLauncher(f);
+            output[0] = fromLauncher;
+            return fromLauncher != null;
+        }, timeout);
+        return (T) output[0];
+    }
+
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
     protected void waitForLauncherCondition(
             String message,
             Runnable testThreadAction, Function<Launcher, Boolean> condition,
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index ddcb4da..80bb3ed 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -44,8 +44,11 @@
                 } finally {
                     mTest.mDevice.setOrientationNatural();
                     mTest.executeOnLauncher(launcher ->
-                            launcher.getRotationHelper().forceAllowRotationForTesting(
-                                    false));
+                    {
+                        if (launcher != null) {
+                            launcher.getRotationHelper().forceAllowRotationForTesting(false);
+                        }
+                    });
                     mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
                 }
             }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 0c87ab9..c2a3c1c 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -173,7 +173,6 @@
 
     @Test
     public void testWorkspace() throws Exception {
-        mLauncher.enableDebugTracing();
         final Workspace workspace = mLauncher.getWorkspace();
 
         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
@@ -209,7 +208,6 @@
         // Test starting a workspace app.
         final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
         assertNotNull("No Chrome app in workspace", app);
-        mLauncher.disableDebugTracing();
     }
 
     public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -300,7 +298,6 @@
     @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.
@@ -317,13 +314,11 @@
                 "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
@@ -343,7 +338,6 @@
         } finally {
             allApps.unfreeze();
         }
-        mLauncher.disableDebugTracing();
     }
 
     public static String getAppPackageName() {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 3206a69..5c38c8d 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -69,34 +68,22 @@
     }
 
     @Test
+    @PortraitLandscape
     public void testWidgetConfig() throws Throwable {
-        runTest(false, true);
+        runTest(true);
     }
 
     @Test
-    @Ignore // b/121280703
-    public void testWidgetConfig_rotate() throws Throwable {
-        runTest(true, true);
-    }
-
-    @Test
+    @PortraitLandscape
     public void testConfigCancelled() throws Throwable {
-        runTest(false, false);
+        runTest(false);
     }
 
-    @Test
-    @Ignore // b/121280703
-    public void testConfigCancelled_rotate() throws Throwable {
-        runTest(true, false);
-    }
 
     /**
-     * @param rotateConfig should the config screen be rotated
      * @param acceptConfig accept the config activity
      */
-    private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable {
-        lockRotation(true);
-
+    private void runTest(boolean acceptConfig) throws Throwable {
         clearHomescreen();
         mDevice.pressHome();
 
@@ -110,13 +97,6 @@
         // Widget id for which the config activity was opened
         mWidgetId = monitor.getWidgetId();
 
-        if (rotateConfig) {
-            // Rotate the screen and verify that the config activity is recreated
-            monitor = new WidgetConfigStartupMonitor();
-            lockRotation(false);
-            assertEquals(mWidgetId, monitor.getWidgetId());
-        }
-
         // Verify that the widget id is valid and bound
         assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
 
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 276c614..b9abc2e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -26,7 +26,6 @@
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,19 +40,8 @@
     @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     @Test
-    public void testDragIcon_portrait() throws Throwable {
-        lockRotation(true);
-        performTest();
-    }
-
-    @Test
-    @Ignore // b/121280703
-    public void testDragIcon_landscape() throws Throwable {
-        lockRotation(false);
-        performTest();
-    }
-
-    private void performTest() throws Throwable {
+    @PortraitLandscape
+    public void testDragIcon() throws Throwable {
         clearHomescreen();
         mDevice.pressHome();
 
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index a9a5090..07129dd 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -128,8 +128,6 @@
         if (!Utilities.ATLEAST_OREO) {
             return;
         }
-        lockRotation(true);
-
         clearHomescreen();
         mDevice.pressHome();
 
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index eef2f24..cdda0f0 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -15,17 +15,16 @@
 
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
-    private static int sScreenshotCount = 0;
     final private UiDevice mDevice;
 
     public FailureWatcher(UiDevice device) {
         mDevice = device;
     }
 
-    private void dumpViewHierarchy() {
+    private static void dumpViewHierarchy(UiDevice device) {
         final ByteArrayOutputStream stream = new ByteArrayOutputStream();
         try {
-            mDevice.dumpWindowHierarchy(stream);
+            device.dumpWindowHierarchy(stream);
             stream.flush();
             stream.close();
             for (String line : stream.toString().split("\\r?\\n")) {
@@ -38,22 +37,27 @@
 
     @Override
     protected void failed(Throwable e, Description description) {
-        if (mDevice == null) return;
+        onError(mDevice, description, e);
+    }
+
+    public static void onError(UiDevice device, Description description, Throwable e) {
+        if (device == null) return;
         final String pathname = getInstrumentation().getTargetContext().
-                getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
+                getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
+                + ".png";
         Log.e(TAG, "Failed test " + description.getMethodName() +
                 ", screenshot will be saved to " + pathname +
                 ", track trace is below, UI object dump is further below:\n" +
                 Log.getStackTraceString(e));
-        dumpViewHierarchy();
+        dumpViewHierarchy(device);
 
         try {
-            final String dumpsysResult = mDevice.executeShellCommand(
+            final String dumpsysResult = device.executeShellCommand(
                     "dumpsys activity service TouchInteractionService");
             Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
         } catch (IOException ex) {
         }
 
-        mDevice.takeScreenshot(new File(pathname));
+        device.takeScreenshot(new File(pathname));
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 82af7b0..df80a51 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import android.graphics.Point;
 
 import androidx.test.uiautomator.By;
@@ -23,13 +25,10 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
-
 /**
  * Ancestor for AppIcon and AppMenuItem.
  */
 abstract class Launchable {
-    private static final int WAIT_TIME_MS = 60000;
     protected final LauncherInstrumentation mLauncher;
 
     protected final UiObject2 mObject;
@@ -53,9 +52,12 @@
     private Background launch(BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
-        mLauncher.assertTrue(
-                "Launching an app didn't open a new window: " + mObject.getText(),
-                mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+
+        mLauncher.executeAndWaitForEvent(
+                () -> mObject.click(),
+                event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                "Launching an app didn't open a new window: " + mObject.getText());
+
         mLauncher.assertTrue(
                 "App didn't start: " + selector,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 6348c41..14f2c85 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -124,7 +124,7 @@
     private static final String APPS_RES_ID = "apps_view";
     private static final String OVERVIEW_RES_ID = "overview_panel";
     private static final String WIDGETS_RES_ID = "widgets_list_view";
-    public static final int WAIT_TIME_MS = 60000;
+    public static final int WAIT_TIME_MS = 10000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
 
     private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -763,8 +763,7 @@
         final Bundle parcel = (Bundle) executeAndWaitForEvent(
                 () -> linearGesture(startX, startY, endX, endY, steps),
                 event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
-                "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
-                        + ", " + endX + ", " + endY);
+                "Swipe failed to receive an event for the swipe end");
         assertEquals("Swipe switched launcher to a wrong state;",
                 TestProtocol.stateOrdinalToString(expectedState),
                 TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
@@ -882,6 +881,7 @@
     }
 
     long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
+        log("movePointer: " + from + " to " + to);
         final Point point = new Point();
         long steps = duration / GESTURE_STEP_MS;
         long currentTime = startTime;
@@ -956,4 +956,9 @@
     public void disableDebugTracing() {
         getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
     }
+
+    public int getTotalPssKb() {
+        return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB).
+                getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 6e33322..91f0fc4 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,18 +16,16 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import android.graphics.Rect;
 
 import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * A recent task in the overview panel carousel.
  */
 public final class OverviewTask {
-    private static final long WAIT_TIME_MS = 60000;
     private final LauncherInstrumentation mLauncher;
     private final UiObject2 mTask;
     private final BaseOverview mOverview;
@@ -66,9 +64,11 @@
         verifyActiveContainer();
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "clicking an overview task")) {
-            mLauncher.assertTrue("Launching task didn't open a new window: " +
-                            mTask.getParent().getContentDescription(),
-                    mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+            mLauncher.executeAndWaitForEvent(
+                    () -> mTask.click(),
+                    event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    "Launching task didn't open a new window: " +
+                            mTask.getParent().getContentDescription());
         }
         return new Background(mLauncher);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 2495933..f3295d0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.fail;
 
 import android.graphics.Point;
+import android.graphics.Rect;
 
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
@@ -31,7 +32,8 @@
  * All widgets container.
  */
 public final class Widgets extends LauncherInstrumentation.VisibleContainer {
-    private static final int FLING_SPEED = 1500;
+    private static final Rect MARGINS = new Rect(100, 100, 100, 100);
+    private static final int FLING_STEPS = 10;
 
     Widgets(LauncherInstrumentation launcher) {
         super(launcher);
@@ -46,11 +48,7 @@
                 "want to fling forward in widgets")) {
             LauncherInstrumentation.log("Widgets.flingForward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
-            widgetsContainer.setGestureMargins(0, 0, 0,
-                    ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
-                            mLauncher.getResources()) + 1);
-            widgetsContainer.fling(Direction.DOWN,
-                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
                 verifyActiveContainer();
             }
@@ -66,10 +64,7 @@
                 "want to fling backwards in widgets")) {
             LauncherInstrumentation.log("Widgets.flingBackward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
-            widgetsContainer.setGestureMargin(100);
-            widgetsContainer.fling(Direction.UP,
-                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-            mLauncher.waitForIdle();
+            mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();
             }
@@ -101,7 +96,7 @@
                 return new Widget(mLauncher, widget);
             }
             if (++i > 40) fail("Too many attempts");
-            widgetsContainer.scroll(Direction.DOWN, 1f);
+            mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, 50);
         }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 639902f..510ea14 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -21,6 +21,7 @@
 import static junit.framework.TestCase.assertTrue;
 
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.SystemClock;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -40,6 +41,7 @@
     private static final float FLING_SPEED =
             LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
     private static final int DRAG_DURACTION = 2000;
+    private static final int FLING_STEPS = 10;
     private final UiObject2 mHotseat;
 
     Workspace(LauncherInstrumentation launcher) {
@@ -180,9 +182,9 @@
      */
     public void flingForward() {
         final UiObject2 workspace = verifyActiveContainer();
-        workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
-        workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
+        mLauncher.scroll(workspace, Direction.RIGHT, 1f,
+                new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
+                FLING_STEPS);
         verifyActiveContainer();
     }
 
@@ -192,9 +194,9 @@
      */
     public void flingBackward() {
         final UiObject2 workspace = verifyActiveContainer();
-        workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
-        workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
+        mLauncher.scroll(workspace, Direction.LEFT, 1f,
+                new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
+                FLING_STEPS);
         verifyActiveContainer();
     }