Merging ub-launcher3-qt-qpr1-dev, build 5872416

Test: Manual

Bug:121280703 P2 Finish implementation of PortraitLandscape annotation for tests
Bug:135633159 P3 [QPR] Promise icons on home screen
Bug:135638690 P1 [QPR] On-device app search
Bug:137106918 P2 UX: Accidental NGA triggering when hitting Undo actions in apps
Bug:137200188 P3 Home screen app title disappears during animation
Bug:138195597 P2 Wrong icon animated on return to home screen
Bug:138396187 P1 Swipe and hold from an app no longer uses spring to animate adjacent task
Bug:138729157 P1 [Flaky test] java.lang.AssertionError: http://go/tapl : want to fling backwards in widgets, flung back, but the current state is not WIDGETS; Unexpected launcher object visible: workspace
Bug:138729456 P1 [Flaky test] java.lang.AssertionError: http://go/tapl : Can't find an object with selector: BySelector [CLASS='\Qandroid.widget.TextView\E', PKG='\Qcom.google.android.apps.nexuslauncher\E', TEXT='\QShortcut 3\E'] (visible state: Workspace)
Bug:138964490 P1 Support DeviceConfig to drive ToggleableFlags
Bug:139021165 P2 [TEST TRACKER] [QPR] Promise icons on home screen
Bug:139137636 P2 Create memory tests for Launcher
Bug:139551306 P4 [Polish] Reduce shelf paddings in Overview
Bug:139885365 P3 App open animation different between launcher suggested apps and launcher home screen/drawer
Bug:139918680 P2 [a11y] Talkback shouldn't keep focusing on the background item and speak the description of the background item again after entering Widget list.
Bug:140076379 P1 Launcher ub-launcher3-qt-qpr1-dev Branch Build Keeps Crashing due to Exception
Bug:140252951 P2 Add widget launch test.
Bug:140308849 P2 Jank during swipe up to home, especially noticeable after pausing first
Bug:140311911 P2 Flake in Launcher tests: java.lang.AssertionError: Stable state != state: OverviewState, LauncherState
Bug:140405990 P2 [a11y] Unable to add shortcut to Home screen by Voice access or Switch access.
Bug:140819614 P1 If an install session is abandoned for an already installed app, the corresponding icon is removed
Bug:140823188 P1 AppPredictionsUITests failing
Bug:140837771 P1 Failing test: AddConfigWidgetTests and AddWidgetTests are failing
Change-Id: I1efae6216c53b1fee3e105c9356ed43c4bf46c6e
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/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 371161e..7115943 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -18,16 +18,11 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
 
-import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -35,18 +30,17 @@
 import android.content.Context;
 import android.view.View;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
  * {@link RecentsView}.
@@ -156,8 +150,11 @@
                 return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
                         RecentsView.CONTENT_ALPHA, values);
             case INDEX_RECENTS_TRANSLATE_X_ANIM:
-                return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
-                        VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
+                return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
+                        .setDampingRatio(0.8f)
+                        .setStiffness(250)
+                        .setValues(values)
+                        .build(mLauncher);
             default:
                 return super.createStateElementAnimation(index, values);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 085bbc4..1a59770 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -24,7 +24,6 @@
 import android.app.prediction.AppTarget;
 import android.content.ComponentName;
 import android.content.Context;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -32,6 +31,8 @@
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
@@ -58,7 +59,7 @@
  * 4) Maintains the current active client id (for the predictions) and all updates are performed on
  * that client id.
  */
-public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
+public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
         OnIDPChangeListener, OnUpdateListener {
 
     public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
@@ -153,7 +154,10 @@
     public void reapplyItemInfo(ItemInfoWithIcon info) { }
 
     @Override
-    public void onGlobalLayout() {
+    public void onStateTransitionStart(LauncherState toState) { }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState state) {
         if (mAppsView == null) {
             return;
         }
@@ -162,7 +166,8 @@
             mPendingState = null;
         }
         if (mPendingState == null) {
-            mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+            Launcher.getLauncher(mAppsView.getContext()).getStateManager()
+                    .removeStateListener(this);
         }
     }
 
@@ -170,9 +175,8 @@
         boolean registerListener = mPendingState == null;
         mPendingState = state;
         if (registerListener) {
-            // OnGlobalLayoutListener is called whenever a view in the view tree changes
-            // visibility. Add a listener and wait until appsView is invisible again.
-            mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+            // Add a listener and wait until appsView is invisible again.
+            Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
         }
     }
 
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/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/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
index 38b5a13..346969e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -82,7 +83,8 @@
     private int mDirection;
     private ActivityControlHelper mActivityControlHelper;
 
-    private final float mDistThreshold;
+    private final float mDragDistThreshold;
+    private final float mFlingDistThreshold;
     private final long mTimeThreshold;
     private final int mAngleThreshold;
     private final float mSquaredSlop;
@@ -97,7 +99,8 @@
         final Resources res = context.getResources();
         mContext = context;
         mSysUiProxy = systemUiProxy;
-        mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+        mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+        mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
         mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
         mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
 
@@ -117,8 +120,6 @@
     @Override
     public void onMotionEvent(MotionEvent ev) {
         // TODO add logging
-        mGestureDetector.onTouchEvent(ev);
-
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
                 mActivePointerId = ev.getPointerId(0);
@@ -213,6 +214,8 @@
                 break;
         }
 
+        mGestureDetector.onTouchEvent(ev);
+
         if (mState != STATE_ACTIVE) {
             mDelegate.onMotionEvent(ev);
         }
@@ -220,9 +223,9 @@
 
     private void updateAssistantProgress() {
         if (!mLaunchedAssistant) {
-            mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
+            mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
             try {
-                if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
+                if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
                     mSysUiProxy.onAssistantGestureCompletion(0);
                     startAssistantInternal(SWIPE);
 
@@ -271,7 +274,9 @@
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
             if (isValidAssistantGestureAngle(velocityX, -velocityY)
-                && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
+                && mDistance >= mFlingDistThreshold
+                && !mLaunchedAssistant
+                && mState != STATE_DELEGATE_ACTIVE) {
                 mLastProgress = 1;
                 try {
                     mSysUiProxy.onAssistantGestureCompletion(
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/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b0968f9..78424ca 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -69,6 +69,7 @@
     <!-- Distance from the vertical edges of the screen in which assist gestures are recognized -->
     <dimen name="gestures_assistant_width">48dp</dimen>
     <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
+    <dimen name="gestures_assistant_fling_threshold">55dp</dimen>
 
     <!-- Distance to move elements when swiping up to go home from launcher -->
     <dimen name="home_pullback_distance">28dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
index e425088..853a1c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,17 +16,34 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.content.Context;
 import android.provider.DeviceConfig;
 import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
 
 public class TogglableFlag extends BaseTogglableFlag {
+    public static final String NAMESPACE_LAUNCHER = "launcher";
+    public static final String TAG = "TogglableFlag";
 
     public TogglableFlag(String key, boolean defaultValue, String description) {
         super(key, defaultValue, description);
     }
 
     @Override
-    public boolean getInitialValue(boolean value) {
-        return DeviceConfig.getBoolean("launcher", getKey(), value);
+    public boolean getOverridenDefaultValue(boolean value) {
+        return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
+    }
+
+    @Override
+    public void addChangeListener(Context context, Runnable r) {
+        DeviceConfig.addOnPropertiesChangedListener(
+            NAMESPACE_LAUNCHER,
+            context.getMainExecutor(),
+            (properties) -> {
+                if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
+                    return;
+                }
+                initialize(context);
+                r.run();
+            });
     }
 }
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/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 edd5011..e295527 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -118,7 +118,6 @@
                         assertTrue("Couldn't set overlay",
                                 setActiveOverlay(prevOverlayPkg, originalMode));
                     }
-                    mLauncher.disableDebugTracing();
                 }
 
                 private void evaluateWithThreeButtons() throws Throwable {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index be8506d..885fdbf 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -97,7 +97,6 @@
     @Test
     @PortraitLandscape
     public void testOverview() throws Exception {
-        mLauncher.enableDebugTracing();
         startTestApps();
         Overview overview = mLauncher.pressHome().switchToOverview();
         assertTrue("Launcher internal state didn't switch to Overview",
@@ -177,7 +176,6 @@
         executeOnLauncher(
                 launcher -> assertEquals("Still have tasks after dismissing all",
                         0, getTaskCount(launcher)));
-        mLauncher.disableDebugTracing();
     }
 
     private int getCurrentOverviewPage(Launcher launcher) {
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/Launcher.java b/src/com/android/launcher3/Launcher.java
index d79230f..257f0df 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -157,6 +157,7 @@
 import java.util.function.Predicate;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 /**
  * Default launcher application.
@@ -209,9 +210,9 @@
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
 
     // How long to wait before the new-shortcut animation automatically pans the workspace
-    private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+    @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
-    @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+    @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
 
     private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
     private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2a801d6..b4a2216 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -94,6 +94,7 @@
         if (FeatureFlags.IS_DOGFOOD_BUILD) {
             filter.addAction(ACTION_FORCE_ROLOAD);
         }
+        FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
 
         mContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(mContext).enableAndResetCache();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d79f5d5..a041489 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
 
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
@@ -51,6 +52,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
@@ -211,6 +213,30 @@
         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
     }
 
+    public void onSessionFailure(String packageName, UserHandle user) {
+        enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
+                synchronized (dataModel) {
+                    for (ItemInfo info : dataModel.itemsIdMap) {
+                        if (info instanceof WorkspaceItemInfo
+                                && ((WorkspaceItemInfo) info).hasPromiseIconUi()
+                                && user.equals(info.user)
+                                && info.getIntent() != null
+                                && TextUtils.equals(packageName, info.getIntent().getPackage())) {
+                            removedIds.put(info.id, true /* remove */);
+                        }
+                    }
+                }
+
+                if (!removedIds.isEmpty()) {
+                    deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+                }
+            }
+        });
+    }
+
     @Override
     public void onPackageRemoved(String packageName, UserHandle user) {
         onPackagesRemoved(user, packageName);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index e1ea3be..63914b0 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -228,8 +228,9 @@
     private void goToState(LauncherState state, boolean animated, long delay,
             final Runnable onCompleteRunnable) {
         if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " + state + " @ " +
-                    Log.getStackTraceString(new Throwable()));
+            Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
+                    state.getClass().getSimpleName() +
+                    " @ " + Log.getStackTraceString(new Throwable()));
         }
         animated &= Utilities.areAnimationsEnabled(mLauncher);
         if (mLauncher.isInState(state)) {
@@ -411,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);
 
@@ -431,7 +437,9 @@
             mLastStableState = state.getHistoryForState(mCurrentStableState);
             mCurrentStableState = state;
             if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " + state);
+                Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
+                        state.getClass().getSimpleName() +
+                        " @ " + Log.getStackTraceString(new Throwable()));
             }
         }
 
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0c1303b..0c12c60 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,5 +1,7 @@
 package com.android.launcher3.accessibility;
 
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
 import static com.android.launcher3.LauncherState.NORMAL;
 
 import android.app.AlertDialog;
@@ -30,14 +32,14 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.keyboard.CustomActionsPopup;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ShortcutUtil;
@@ -164,6 +166,13 @@
     }
 
     public boolean performAction(final View host, final ItemInfo item, int action) {
+        if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) {
+            CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
+            if (popup.canShow()) {
+                popup.show();
+                return true;
+            }
+        }
         if (action == MOVE) {
             beginAccessibleDrag(host, item);
         } else if (action == ADD_TO_WORKSPACE) {
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/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/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
new file mode 100644
index 0000000..0f34c1e
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -0,0 +1,229 @@
+/*
+ * 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.anim;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.FloatProperty;
+
+import com.android.launcher3.util.DefaultDisplay;
+
+import androidx.annotation.FloatRange;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Utility class to build an object animator which follows the same path as a spring animation for
+ * an underdamped spring.
+ */
+public class SpringAnimationBuilder<T> extends FloatProperty<T> {
+
+    private final T mTarget;
+    private final FloatProperty<T> mProperty;
+
+    private float mStartValue;
+    private float mEndValue;
+    private float mVelocity = 0;
+
+    private float mStiffness = SpringForce.STIFFNESS_MEDIUM;
+    private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+    private float mMinVisibleChange = 1;
+
+    // Multiplier to the min visible change value for value threshold
+    private static final float THRESHOLD_MULTIPLIER = 0.65f;
+
+    /**
+     * The spring equation is given as
+     *   x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t)
+     *   v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t)
+     *                  + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2
+     *
+     *   a = x(0)
+     *   b = beta * x(0) / (2 * gamma) + v(0) / gamma
+     */
+    private double beta;
+    private double gamma;
+
+    private double a, b;
+    private double va, vb;
+
+    // Threshold for velocity and value to determine when it's reasonable to assume that the spring
+    // is approximately at rest.
+    private double mValueThreshold;
+    private double mVelocityThreshold;
+
+    private float mCurrentTime = 0;
+
+    public SpringAnimationBuilder(T target, FloatProperty<T> property) {
+        super("dynamic-spring-property");
+        mTarget = target;
+        mProperty = property;
+
+        mStartValue = mProperty.get(target);
+    }
+
+    public SpringAnimationBuilder<T> setEndValue(float value) {
+        mEndValue = value;
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setStartValue(float value) {
+        mStartValue = value;
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setValues(float... values) {
+        if (values.length > 1) {
+            mStartValue = values[0];
+            mEndValue = values[values.length - 1];
+        } else {
+            mEndValue = values[0];
+        }
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setStiffness(
+            @FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
+        if (stiffness <= 0) {
+            throw new IllegalArgumentException("Spring stiffness constant must be positive.");
+        }
+        mStiffness = stiffness;
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setDampingRatio(
+            @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+                    float dampingRatio) {
+        if (dampingRatio <= 0 || dampingRatio >= 1) {
+            throw new IllegalArgumentException("Damping ratio must be between 0 and 1");
+        }
+        mDampingRatio = dampingRatio;
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setMinimumVisibleChange(
+            @FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
+        if (minimumVisibleChange <= 0) {
+            throw new IllegalArgumentException("Minimum visible change must be positive.");
+        }
+        mMinVisibleChange = minimumVisibleChange;
+        return this;
+    }
+
+    public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) {
+        mVelocity = startVelocity;
+        return this;
+    }
+
+    @Override
+    public void setValue(T object, float time) {
+        mCurrentTime = time;
+        mProperty.setValue(
+                object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
+    }
+
+    @Override
+    public Float get(T t) {
+        return mCurrentTime;
+    }
+
+    public ObjectAnimator build(Context context) {
+        int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
+        double naturalFreq = Math.sqrt(mStiffness);
+        double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
+
+        // All the calculations assume the stable position to be 0, shift the values accordingly.
+        beta = 2 * mDampingRatio * naturalFreq;
+        gamma = dampedFreq;
+        a =  mStartValue - mEndValue;
+        b = beta * a / (2 * gamma) + mVelocity / gamma;
+
+        va = a * beta / 2 - b * gamma;
+        vb = a * gamma + beta * b / 2;
+
+        mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER;
+
+        // This multiplier is used to calculate the velocity threshold given a certain value
+        // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
+        // then the velocity is a reasonable threshold.
+        mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs;
+
+        // Find the duration (in seconds) for the spring to reach equilibrium.
+        // equilibrium is reached when x = 0
+        double duration = Math.atan2(-a, b) / gamma;
+
+        // Keep moving ahead until the velocity reaches equilibrium.
+        double piByG = Math.PI / gamma;
+        while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration))
+                >= mVelocityThreshold) {
+            duration += piByG;
+        }
+
+        // Find the shortest time
+        double edgeTime = Math.max(0, duration - piByG / 2);
+        double minDiff = singleFrameMs / 2000.0;    // Half frame time in seconds
+
+        do {
+            if ((duration - edgeTime) < minDiff) {
+                break;
+            }
+            double mid = (edgeTime + duration) / 2;
+            if (isAtEquilibrium(mid)) {
+                duration = mid;
+            } else {
+                edgeTime = mid;
+            }
+        } while (true);
+
+
+        long durationMs = (long) (1000.0 * duration);
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
+        animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
+        animator.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                mProperty.setValue(mTarget, mEndValue);
+            }
+        });
+        return animator;
+    }
+
+    private boolean isAtEquilibrium(double t) {
+        double ec = exponentialComponent(t);
+
+        if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) {
+            return false;
+        }
+        return Math.abs(ec * cosSinV(t)) < mVelocityThreshold;
+    }
+
+    private double exponentialComponent(double t) {
+        return Math.pow(Math.E, - beta * t / 2);
+    }
+
+    private double cosSinX(double t) {
+        return cosSin(t, a, b);
+    }
+
+    private double cosSinV(double t) {
+        return cosSin(t, va, vb);
+    }
+
+    private double cosSin(double t, double cosFactor, double sinFactor) {
+        double angle = t * gamma;
+        return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle);
+    }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index e1f17cf..ee9da73 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -136,6 +136,30 @@
         }
     }
 
+    /**
+     * Add a promise app icon to the workspace iff:
+     * - The settings for it are enabled
+     * - The user installed the app
+     * - There is an app icon and label (For apps with no launching activity, no icon is provided).
+     * - The app is not already installed
+     * - A promise icon for the session has not already been created
+     */
+    private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
+        if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+                && SessionCommitReceiver.isEnabled(mAppContext)
+                && verify(sessionInfo) != null
+                && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
+                && sessionInfo.getAppIcon() != null
+                && !TextUtils.isEmpty(sessionInfo.getAppLabel())
+                && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+                && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
+                        getUserHandle(sessionInfo)) == null) {
+            SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
+            mPromiseIconIds.add(sessionInfo.getSessionId());
+            updatePromiseIconPrefs();
+        }
+    }
+
     private final SessionCallback mCallback = new SessionCallback() {
 
         @Override
@@ -149,16 +173,7 @@
                 }
             }
 
-            if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
-                    && SessionCommitReceiver.isEnabled(mAppContext)
-                    && sessionInfo != null
-                    && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER) {
-                SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
-                if (!mPromiseIconIds.contains(sessionInfo.getSessionId())) {
-                    mPromiseIconIds.add(sessionInfo.getSessionId());
-                    updatePromiseIconPrefs();
-                }
-            }
+            tryQueuePromiseAppIcon(sessionInfo);
         }
 
         @Override
@@ -173,12 +188,14 @@
                 sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
                         packageName, key.mUser));
 
-                if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
+                if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+                        && mPromiseIconIds.contains(sessionId)) {
                     LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
                     if (appState != null) {
-                        LauncherModel model = appState.getModel();
-                        model.onPackageRemoved(packageName, key.mUser);
+                        appState.getModel().onSessionFailure(packageName, key.mUser);
                     }
+                    // If it is successful, the id is removed in the the package added flow.
+                    removePromiseIconId(sessionId);
                 }
             }
         }
@@ -196,7 +213,10 @@
 
         @Override
         public void onBadgingChanged(int sessionId) {
-            pushSessionDisplayToLauncher(sessionId);
+            SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+            if (sessionInfo != null) {
+                tryQueuePromiseAppIcon(sessionInfo);
+            }
         }
 
         private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index ea6261a..025087b 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -143,6 +143,8 @@
 
     public static abstract class BaseTogglableFlag {
         private final String key;
+        // should be value that is hardcoded in client side.
+        // Comparatively, getDefaultValue() can be overridden.
         private final boolean defaultValue;
         private final String description;
         private boolean currentValue;
@@ -152,8 +154,9 @@
                 boolean defaultValue,
                 String description) {
             this.key = checkNotNull(key);
-            this.currentValue = this.defaultValue = getInitialValue(defaultValue);
+            this.currentValue = this.defaultValue = defaultValue;
             this.description = checkNotNull(description);
+
             synchronized (sLock) {
                 sFlags.add((TogglableFlag)this);
             }
@@ -169,16 +172,18 @@
             return key;
         }
 
-        void initialize(Context context) {
-            currentValue = getFromStorage(context, defaultValue);
+        protected void initialize(Context context) {
+            currentValue = getFromStorage(context, getDefaultValue());
         }
 
-        protected abstract boolean getInitialValue(boolean value);
+        protected abstract boolean getOverridenDefaultValue(boolean value);
+
+        protected abstract void addChangeListener(Context context, Runnable r);
 
         public void updateStorage(Context context, boolean value) {
             SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
                     Context.MODE_PRIVATE).edit();
-            if (value == defaultValue) {
+            if (value == getDefaultValue()) {
                 editor.remove(key).apply();
             } else {
                 editor.putBoolean(key, value).apply();
@@ -187,11 +192,11 @@
 
         boolean getFromStorage(Context context, boolean defaultValue) {
             return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
-                    .getBoolean(key, defaultValue);
+                    .getBoolean(key, getDefaultValue());
         }
 
         boolean getDefaultValue() {
-            return defaultValue;
+            return getOverridenDefaultValue(defaultValue);
         }
 
         /** Returns the value of the flag at process start, including any overrides present. */
@@ -208,6 +213,8 @@
             return "TogglableFlag{"
                     + "key=" + key + ", "
                     + "defaultValue=" + defaultValue + ", "
+                    + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
+                    + "currentValue=" + currentValue + ", "
                     + "description=" + description
                     + "}";
         }
@@ -220,7 +227,7 @@
             if (o instanceof TogglableFlag) {
                 BaseTogglableFlag that = (BaseTogglableFlag) o;
                 return (this.key.equals(that.getKey()))
-                        && (this.defaultValue == that.getDefaultValue())
+                        && (this.getDefaultValue() == that.getDefaultValue())
                         && (this.description.equals(that.getDescription()));
             }
             return false;
@@ -232,7 +239,7 @@
             h$ *= 1000003;
             h$ ^= key.hashCode();
             h$ *= 1000003;
-            h$ ^= defaultValue ? 1231 : 1237;
+            h$ ^= getDefaultValue() ? 1231 : 1237;
             h$ *= 1000003;
             h$ ^= description.hashCode();
             return h$;
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/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 55d58b9..abff237 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
 import com.android.launcher3.icons.cache.BaseIconCache;
 import com.android.launcher3.icons.cache.CachingLogic;
@@ -237,7 +238,8 @@
 
     @Override
     protected String getIconSystemState(String packageName) {
-        return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
+        return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
+                + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
     }
 
     public static abstract class IconLoadRequest extends HandlerRunnable {
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/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 4fd0f88..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;
@@ -113,13 +114,13 @@
                 break;
             }
 
-            case TestProtocol.REQUEST_ALLOCATED_MEMORY: {
-                final Runtime runtime = Runtime.getRuntime();
-                response.putLong(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        runtime.totalMemory() - runtime.freeMemory());
+            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 520598a..60a59dd 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -73,7 +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_ALLOCATED_MEMORY = "allocated-memory";
+    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";
@@ -83,4 +83,5 @@
     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/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 792d69f..af99713 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -23,37 +23,57 @@
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 public class ShortcutUtil {
-  public static boolean supportsShortcuts(ItemInfo info) {
-    return isActive(info) && (isApp(info) || isPinnedShortcut(info));
-  }
+    /**
+     * Returns true when we should show shortcut menu for the item.
+     */
+    public static boolean supportsShortcuts(ItemInfo info) {
+        return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+    }
 
-  public static boolean supportsDeepShortcuts(ItemInfo info) {
-    return isActive(info) && isApp(info);
-  }
+    /**
+     * Returns true when we should show depp shortcuts in shortcut menu for the item.
+     */
+    public static boolean supportsDeepShortcuts(ItemInfo info) {
+        return isActive(info) && isApp(info);
+    }
 
-  public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
-    return isActive(info) && isPinnedShortcut(info) ?
-        ShortcutKey.fromItemInfo(info).getId() : null;
-  }
+    /**
+     * Returns the shortcut id if the item is a pinned shortcut.
+     */
+    public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
+        return isActive(info) && isPinnedShortcut(info)
+                ? ShortcutKey.fromItemInfo(info).getId() : null;
+    }
 
-  public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
-    return isActive(info) && isPinnedShortcut(info) ?
-        ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
-  }
+    /**
+     * Returns the person keys associated with the item. (Has no function right now.)
+     */
+    public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
+        return isActive(info) && isPinnedShortcut(info)
+                ? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
+    }
 
-  private static boolean isActive(ItemInfo info) {
-    boolean isLoading = info instanceof WorkspaceItemInfo
-        && ((WorkspaceItemInfo) info).hasPromiseIconUi();
-    return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
-  }
+    /**
+     * Returns true if the item is a deep shortcut.
+     */
+    public static boolean isDeepShortcut(ItemInfo info) {
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && info instanceof WorkspaceItemInfo;
+    }
 
-  private static boolean isApp(ItemInfo info) {
-    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-  }
+    private static boolean isActive(ItemInfo info) {
+        boolean isLoading = info instanceof WorkspaceItemInfo
+                && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+        return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+    }
 
-  private static boolean isPinnedShortcut(ItemInfo info) {
-    return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-        && info.container != ItemInfo.NO_ID
-        && info instanceof WorkspaceItemInfo;
-  }
+    private static boolean isApp(ItemInfo info) {
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+    }
+
+    private static boolean isPinnedShortcut(ItemInfo info) {
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && info.container != ItemInfo.NO_ID
+                && info instanceof WorkspaceItemInfo;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/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..f728a67 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);
@@ -716,7 +721,7 @@
      */
     @UiThread
     public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
-        IconLoadResult result = new IconLoadResult();
+        IconLoadResult result = new IconLoadResult(info);
         new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
             RectF position = new RectF();
             getLocationBoundsForView(l, v, isOpening, position);
@@ -745,10 +750,13 @@
 
         // Get the drawable on the background thread
         boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
-        view.mIconLoadResult = sIconLoadResult;
-        if (shouldLoadIcon && view.mIconLoadResult == null) {
-            view.mIconLoadResult = fetchIcon(launcher, originalView,
-                    (ItemInfo) originalView.getTag(), isOpening);
+        if (shouldLoadIcon) {
+            if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) {
+                view.mIconLoadResult = sIconLoadResult;
+            } else {
+                view.mIconLoadResult = fetchIcon(launcher, originalView,
+                        (ItemInfo) originalView.getTag(), isOpening);
+            }
         }
         sIconLoadResult = null;
 
@@ -776,7 +784,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 +817,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));
         }
 
@@ -890,10 +898,15 @@
     }
 
     private static class IconLoadResult {
+        final ItemInfo itemInfo;
         Drawable drawable;
         Drawable badge;
         int iconOffset;
         Runnable onIconLoaded;
         boolean isIconLoaded;
+
+        public IconLoadResult(ItemInfo itemInfo) {
+            this.itemInfo = itemInfo;
+        }
     }
 }
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_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
index e875a3c..60f12d8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.content.Context;
 import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
 
 public class TogglableFlag extends BaseTogglableFlag {
@@ -25,7 +26,10 @@
     }
 
     @Override
-    public boolean getInitialValue(boolean value) {
+    public boolean getOverridenDefaultValue(boolean value) {
         return value;
     }
+
+    @Override
+    public void addChangeListener(Context context, Runnable r) { }
 }
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
new file mode 100644
index 0000000..efbd9c9
--- /dev/null
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.compat;
+
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.UUID;
+
+
+/**
+ * Test to verify promise icon flow.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class PromiseIconUiTest extends AbstractLauncherUiTest {
+
+    private int mSessionId = -1;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+        waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+        mSessionId = -1;
+    }
+
+    @After
+    public void tearDown() {
+        if (mSessionId > -1) {
+            mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+        }
+    }
+
+    /**
+     * Create a session and return the id.
+     */
+    private int createSession(String label, Bitmap icon) throws Throwable {
+        SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName("test.promise.app");
+        params.setAppLabel(label);
+        params.setAppIcon(icon);
+        params.setInstallReason(PackageManager.INSTALL_REASON_USER);
+        return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
+    }
+
+    @Test
+    public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
+        final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+        final Workspace.ItemOperator findPromiseApp = (info, view) ->
+                info != null && TextUtils.equals(info.title, appLabel);
+
+        // Create and add test session
+        mSessionId = createSession(appLabel, Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
+
+        // Verify promise icon is added
+        waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
+
+        // Remove session
+        mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+        mSessionId = -1;
+
+        // Verify promise icon is removed
+        waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+    }
+
+    @Test
+    public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
+        final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+        final Workspace.ItemOperator findPromiseApp = (info, view) ->
+                info != null && TextUtils.equals(info.title, appLabel);
+
+        // Create and add test session without icon or label
+        mSessionId = createSession(null, null);
+
+        // Sleep for duration of animation if a view was to be added + some buffer time.
+        Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500);
+
+        // Verify promise icon is not added
+        waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+    }
+}
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..7f427b3 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -69,34 +69,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 +98,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..cb586ac 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.ui.widget;
 
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import androidx.test.filters.LargeTest;
@@ -22,6 +25,7 @@
 
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.tapl.Widget;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
@@ -41,19 +45,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();
 
@@ -70,5 +63,10 @@
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
                         ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
                                 widgetInfo.provider.getClassName())).call());
+
+        final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
+                DEFAULT_UI_TIMEOUT);
+        assertNotNull("Widget not found on the workspace", widget);
+        widget.launch(getAppPackageName());
     }
 }
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/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9d43352..c6e7dac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -368,7 +368,7 @@
         }
     }
 
-    private void assertEquals(String message, String expected, String actual) {
+    void assertEquals(String message, String expected, String actual) {
         if (!TextUtils.equals(expected, actual)) {
             fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
         }
@@ -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)));
@@ -958,8 +957,8 @@
         getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
     }
 
-    public long getAllocatedMemory() {
-        return getTestInfo(TestProtocol.REQUEST_ALLOCATED_MEMORY).
-                getLong(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    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/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 2495933..7d308af 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();
             }
@@ -82,7 +77,7 @@
         return LauncherInstrumentation.ContainerType.WIDGETS;
     }
 
-    public Widget getWidget(String label) {
+    public Widget getWidget(String labelText) {
         final int margin = ResourceUtils.getNavbarSize(
                 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
         final UiObject2 widgetsContainer = verifyActiveContainer();
@@ -91,17 +86,24 @@
         final Point displaySize = mLauncher.getRealDisplaySize();
 
         int i = 0;
-        final BySelector selector = By.
-                clazz("com.android.launcher3.widget.WidgetCell").
-                hasDescendant(By.text(label));
+        final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
 
         for (; ; ) {
-            final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
-            if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
-                return new Widget(mLauncher, widget);
+            final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
+            if (label != null) {
+                final UiObject2 widget = label.getParent().getParent();
+                mLauncher.assertEquals(
+                        "View is not WidgetCell",
+                        "com.android.launcher3.widget.WidgetCell",
+                        widget.getClassName());
+
+                if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+                    return new Widget(mLauncher, widget);
+                }
             }
+
             if (++i > 40) fail("Too many attempts");
-            widgetsContainer.scroll(Direction.DOWN, 1f);
+            mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, 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();
     }