Merge "Changes page alignment in PagedView calcualtion" into sc-dev
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index ce94305..1f268cc 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -48,10 +48,6 @@
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation animation) {
-        if (config.onlyPlayAtomicComponent()) {
-            return;
-        }
-
         if (SysUINavigationMode.getMode(mLauncher) != TWO_BUTTONS) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index fe8f0c6..249ef3a 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -193,7 +193,6 @@
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation animation) {
         if (mSurface == null
-                || config.onlyPlayAtomicComponent()
                 || config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
                 || mIgnoreStateChangesDuringMultiWindowAnimation) {
             return;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index d65c59e..d36af09 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -26,8 +26,6 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
@@ -83,10 +81,6 @@
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation builder) {
-        if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
-            // The entire recents animation is played atomically.
-            return;
-        }
         if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index bae97d7..0ceb8c7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -28,6 +28,7 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -35,6 +36,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
+import android.view.HapticFeedbackConstants;
 import android.view.View;
 
 import com.android.launcher3.BaseQuickstepLauncher;
@@ -234,7 +236,6 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        getAppsView().getSearchUiManager().destroySearch();
         mHotseatPredictionController.destroy();
     }
 
@@ -251,6 +252,11 @@
                 }
                 break;
             }
+            case HINT_STATE_TWO_BUTTON_ORDINAL: {
+                getStateManager().goToState(OVERVIEW);
+                getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                break;
+            }
             case OVERVIEW_STATE_ORDINAL: {
                 RecentsView rv = getOverviewPanel();
                 sendCustomAccessibilityEvent(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index de7be5d..ba61923 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -18,6 +18,7 @@
 import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.LauncherState.HINT_STATE;
+import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
@@ -115,7 +116,8 @@
                     qsbContainer.setScaleY(0.92f);
                 }
             }
-        } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
+        } else if ((fromState == NORMAL || fromState == HINT_STATE
+                || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
             if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
                 config.setInterpolator(ANIM_WORKSPACE_SCALE,
                         fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 6c71995..e70450d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -108,8 +108,8 @@
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
-        float progressMultiplier = super.initCurrentAnimation(animComponents);
+    protected float initCurrentAnimation() {
+        float progressMultiplier = super.initCurrentAnimation();
         if (mToState == HINT_STATE) {
             // Track the drag across the entire height of the screen.
             progressMultiplier = -1 / getShiftRange();
@@ -203,7 +203,7 @@
     }
 
     private void maybeSwipeInteractionToOverviewComplete() {
-        if (mReachedOverview && mDetector.isSettlingState()) {
+        if (mReachedOverview && !mDetector.isDraggingState()) {
             onSwipeInteractionCompleted(OVERVIEW);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 4766870..61cd13b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -33,6 +33,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
@@ -372,7 +373,7 @@
             // animation as from an app.
             StateAnimationConfig config = new StateAnimationConfig();
             // Update mNonOverviewAnim to do nothing so it doesn't interfere.
-            config.animFlags = 0;
+            config.animFlags = SKIP_ALL_ANIMATIONS;
             updateNonOverviewAnim(targetState, config);
             nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index facfb9d..1af9685 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -23,17 +23,9 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.util.Log;
 import android.view.MotionEvent;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -41,8 +33,6 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.uioverrides.states.OverviewState;
@@ -70,11 +60,6 @@
 
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
-    private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
-
-    // If true, we will finish the current animation instantly on second touch.
-    private boolean mFinishFastOnSecondTouch;
-
     public PortraitStatesTouchController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
         mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
@@ -85,10 +70,6 @@
         // If we are swiping to all apps instead of overview, allow it from anywhere.
         boolean interceptAnywhere = mLauncher.isInState(NORMAL);
         if (mCurrentAnimation != null) {
-            if (mFinishFastOnSecondTouch) {
-                mCurrentAnimation.getAnimationPlayer().end();
-            }
-
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
             if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()
                     || interceptAnywhere) {
@@ -96,11 +77,7 @@
                 // the touch is below the current all apps progress (to allow for double swipe).
                 return true;
             }
-            // Otherwise, make sure everything is settled and don't intercept so they can scroll
-            // recents, dismiss a task, etc.
-            if (mAtomicAnim != null) {
-                mAtomicAnim.end();
-            }
+            // Otherwise, don't intercept so they can scroll recents, dismiss a task, etc.
             return false;
         }
         if (mLauncher.isInState(ALL_APPS)) {
@@ -136,32 +113,6 @@
         return fromState;
     }
 
-    private StateAnimationConfig getNormalToOverviewAnimation() {
-        mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
-
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
-        return builder;
-    }
-
-    private static StateAnimationConfig getOverviewToAllAppsAnimation() {
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                0, ALL_APPS_CONTENT_FADE_THRESHOLD));
-        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
-                RECENTS_FADE_THRESHOLD, 1));
-        return builder;
-    }
-
-    private StateAnimationConfig getAllAppsToOverviewAnimation() {
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
-                1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
-        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
-                0f, 1 - RECENTS_FADE_THRESHOLD));
-        return builder;
-    }
-
     private StateAnimationConfig getNormalToAllAppsAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
         builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
@@ -180,13 +131,7 @@
     protected StateAnimationConfig getConfigForStates(
             LauncherState fromState, LauncherState toState) {
         final StateAnimationConfig config;
-        if (fromState == NORMAL && toState == OVERVIEW) {
-            config = getNormalToOverviewAnimation();
-        } else if (fromState == OVERVIEW && toState == ALL_APPS) {
-            config = getOverviewToAllAppsAnimation();
-        } else if (fromState == ALL_APPS && toState == OVERVIEW) {
-            config = getAllAppsToOverviewAnimation();
-        } else if (fromState == NORMAL && toState == ALL_APPS) {
+        if (fromState == NORMAL && toState == ALL_APPS) {
             config = getNormalToAllAppsAnimation();
         } else if (fromState == ALL_APPS && toState == NORMAL) {
             config = getAllAppsToNormalAnimation();
@@ -197,7 +142,7 @@
     }
 
     @Override
-    protected float initCurrentAnimation(@AnimationFlags int animFlags) {
+    protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
 
@@ -208,7 +153,6 @@
 
         final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
                 : getConfigForStates(mFromState, mToState);
-        config.animFlags = animFlags;
         config.duration = maxAccuracy;
 
         if (mCurrentAnimation != null) {
@@ -243,35 +187,6 @@
     }
 
     @Override
-    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
-            LauncherState targetState, float velocity, boolean isFling) {
-        super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
-                velocity, isFling);
-        handleFirstSwipeToOverview(animator, expectedDuration, targetState, velocity, isFling);
-    }
-
-    private void handleFirstSwipeToOverview(final ValueAnimator animator,
-            final long expectedDuration, final LauncherState targetState, final float velocity,
-            final boolean isFling) {
-        if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
-                && targetState == OVERVIEW) {
-            mFinishFastOnSecondTouch = true;
-        } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
-            mFinishFastOnSecondTouch = true;
-            if (isFling && expectedDuration != 0) {
-                // Update all apps interpolator to add a bit of overshoot starting from currFraction
-                final float currFraction = mCurrentAnimation.getProgressFraction();
-                mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
-                        Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
-                animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
-                        .setInterpolator(LINEAR);
-            }
-        } else {
-            mFinishFastOnSecondTouch = false;
-        }
-    }
-
-    @Override
     protected void onSwipeInteractionCompleted(LauncherState targetState) {
         super.onSwipeInteractionCompleted(targetState);
         if (mStartState == NORMAL && targetState == OVERVIEW) {
@@ -296,16 +211,6 @@
         return launcher.getDragLayer().getHeight() - hotseatHeight;
     }
 
-    private static class InterpolatorWrapper implements Interpolator {
-
-        public TimeInterpolator baseInterpolator = LINEAR;
-
-        @Override
-        public float getInterpolation(float v) {
-            return baseInterpolator.getInterpolation(v);
-        }
-    }
-
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index fc9e1bb..8542209 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -101,7 +101,7 @@
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
+    protected float initCurrentAnimation() {
         StateAnimationConfig config = new StateAnimationConfig();
         setupInterpolators(config);
         config.duration = (long) (getShiftRange() * 2);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index 0ed5291..8f9c014 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -32,8 +32,8 @@
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
-        float multiplier = super.initCurrentAnimation(animComponents);
+    protected float initCurrentAnimation() {
+        float multiplier = super.initCurrentAnimation();
         return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
index faf5054..3f7190f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -17,17 +17,18 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
 import static com.android.launcher3.AbstractFloatingView.getOpenView;
+import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 
 import android.animation.ValueAnimator;
+import android.os.SystemClock;
 import android.view.MotionEvent;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.quickstep.SystemUiProxy;
@@ -91,10 +92,10 @@
         if (mIsTransposed) {
             boolean draggingFromNav =
                     mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
-            return draggingFromNav ? OVERVIEW : NORMAL;
+            return draggingFromNav ? HINT_STATE_TWO_BUTTON : NORMAL;
         } else {
             LauncherState startState = mStartState != null ? mStartState : fromState;
-            return isDragTowardPositive ^ (startState == OVERVIEW) ? OVERVIEW : NORMAL;
+            return isDragTowardPositive ^ (startState == OVERVIEW) ? HINT_STATE_TWO_BUTTON : NORMAL;
         }
     }
 
@@ -104,6 +105,12 @@
         super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
                 velocity, isFling);
         mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
+
+        if (targetState == HINT_STATE_TWO_BUTTON) {
+            // We were going to HINT_STATE_TWO_BUTTON, but end that animation immediately so we go
+            // to OVERVIEW instead.
+            animator.setDuration(0);
+        }
     }
 
     @Override
@@ -113,21 +120,35 @@
     }
 
     @Override
-    protected float initCurrentAnimation(@AnimationFlags int animComponent) {
+    protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
         mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
-                maxAccuracy, animComponent);
+                maxAccuracy);
         return (mLauncher.getDeviceProfile().isSeascape() ? 1 : -1) / range;
     }
 
     @Override
+    protected void updateProgress(float fraction) {
+        super.updateProgress(fraction);
+
+        // We have reached HINT_STATE, end the gesture now to go to OVERVIEW.
+        if (fraction >= 1 && mToState == HINT_STATE_TWO_BUTTON) {
+            final long now = SystemClock.uptimeMillis();
+            MotionEvent event = MotionEvent.obtain(now, now,
+                    MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
+            mDetector.onTouchEvent(event);
+            event.recycle();
+        }
+    }
+
+    @Override
     protected void onSwipeInteractionCompleted(LauncherState targetState) {
         super.onSwipeInteractionCompleted(targetState);
         if (!mIsTransposed) {
             mContinuousTouchCount++;
         }
-        if (mStartState == NORMAL && targetState == OVERVIEW) {
+        if (mStartState == NORMAL && targetState == HINT_STATE_TWO_BUTTON) {
             SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
         } else if (targetState == NORMAL
                 && mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 1a5f9c2..6759936 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -45,6 +45,8 @@
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END;
+import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -1073,7 +1075,7 @@
                         homeAnimFactory, runningTaskTarget, start);
                 mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
                 mSwipePipToHomeAnimator.setInterpolator(interpolator);
-                mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
+                mSwipePipToHomeAnimator.setFloatValues(FRACTION_START, FRACTION_END);
                 mSwipePipToHomeAnimator.start();
                 mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
             } else {
@@ -1483,8 +1485,7 @@
             mRecentsAnimationController.setFinishTaskBounds(
                     mSwipePipToHomeAnimator.getTaskId(),
                     mSwipePipToHomeAnimator.getDestinationBounds(),
-                    mSwipePipToHomeAnimator.getFinishWindowCrop(),
-                    mSwipePipToHomeAnimator.getFinishTransform());
+                    mSwipePipToHomeAnimator.getFinishTransaction());
             mIsSwipingPipToHome = false;
         }
     }
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 1ce4201..311ac83 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -134,7 +135,7 @@
             // to home.
             long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
             return mActivity.getStateManager().createAnimationToNewWorkspace(
-                    NORMAL, accuracy, 0 /* animComponents */);
+                    NORMAL, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index ec585cc..82e8a93 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.graphics.Rect;
+import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
@@ -149,14 +150,13 @@
      * accordingly. This should be called before `finish`
      * @param taskId for which the leash should be updated
      * @param destinationBounds bounds of the final PiP window
-     * @param windowCrop bounds to crop as part of final transform.
-     * @param float9 An array of 9 floats to be used as matrix transform.
+     * @param finishTransaction leash operations for the final transform.
      */
-    public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
-            float[] float9) {
+    public void setFinishTaskBounds(int taskId, Rect destinationBounds,
+            PictureInPictureSurfaceTransaction finishTransaction) {
         UI_HELPER_EXECUTOR.execute(
-                () -> mController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
-                        float9));
+                () -> mController.setFinishTaskBounds(taskId, destinationBounds,
+                        finishTransaction));
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 08503cf..ba1c413 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -146,7 +146,7 @@
                     key.getComponent(), key.userId);
             if (activityInfo != null) {
                 BitmapInfo bitmapInfo = getBitmapInfo(
-                        mIconProvider.getIcon(activityInfo, UserHandle.of(key.userId)),
+                        mIconProvider.getIcon(activityInfo),
                         key.userId,
                         desc.getPrimaryColor(),
                         activityInfo.applicationInfo.isInstantApp());
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 82bfa9b..03a0b4e 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -20,8 +20,6 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -63,10 +61,6 @@
     @Override
     public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config,
             PendingAnimation setter) {
-        if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
-            // The entire recents animation is played atomically.
-            return;
-        }
         if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 1031c5b..1128dac 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -22,13 +22,11 @@
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -105,10 +103,6 @@
 
         StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
         config.duration = duration;
-        config.animFlags = playStaggeredWorkspaceAnim
-                // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
-                ? PLAY_ATOMIC_OVERVIEW_PEEK
-                : ANIM_ALL_COMPONENTS;
         boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
                 .isLayoutNaturalToLauncher();
         config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index f68e936..6757e4c 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 
@@ -184,7 +183,7 @@
      */
     private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
         StateAnimationConfig config = new StateAnimationConfig();
-        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+        config.animFlags = SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
         config.duration = 0;
         // setRecentsAttachedToAppWindow() will animate recents out.
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 0a1a6e8..a1240e0 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -29,6 +29,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -46,10 +47,12 @@
  * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
  * PiP window, which would ideally be on SysUI side as well.
  */
-public class SwipePipToHomeAnimator extends ValueAnimator implements
-        ValueAnimator.AnimatorUpdateListener {
+public class SwipePipToHomeAnimator extends ValueAnimator {
     private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
 
+    public static final float FRACTION_START = 0f;
+    public static final float FRACTION_END = 1f;
+
     private final int mTaskId;
     private final ComponentName mComponentName;
     private final SurfaceControl mLeash;
@@ -139,7 +142,7 @@
                 mHasAnimationEnded = true;
             }
         });
-        addUpdateListener(this);
+        addUpdateListener(this::onAnimationUpdate);
     }
 
     /** sets the from rotation if it's different from the target rotation. */
@@ -167,48 +170,53 @@
                 mAppBounds.top + mDestinationBounds.height());
     }
 
-    @Override
-    public void onAnimationUpdate(ValueAnimator animator) {
+    private void onAnimationUpdate(ValueAnimator animator) {
         if (mHasAnimationEnded) return;
-
-        final float fraction = animator.getAnimatedFraction();
-        final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
-                mDestinationBoundsAnimation);
         final SurfaceControl.Transaction tx =
                 PipSurfaceTransactionHelper.newSurfaceControlTransaction();
-        if (mSourceHintRectInsets == null) {
-            // no source rect hint been set, directly scale the window down
-            onAnimationScale(fraction, tx, bounds);
-        } else {
-            // scale and crop according to the source rect hint
-            onAnimationScaleAndCrop(fraction, tx, bounds);
-        }
-        mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
+        onAnimationUpdate(tx, animator.getAnimatedFraction());
         tx.apply();
     }
 
+    private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx,
+            float fraction) {
+        final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
+                mDestinationBoundsAnimation);
+        final PictureInPictureSurfaceTransaction op;
+        if (mSourceHintRectInsets == null) {
+            // no source rect hint been set, directly scale the window down
+            op = onAnimationScale(fraction, tx, bounds);
+        } else {
+            // scale and crop according to the source rect hint
+            op = onAnimationScaleAndCrop(fraction, tx, bounds);
+        }
+        return op;
+    }
+
     /** scale the window directly with no source rect hint being set */
-    private void onAnimationScale(float fraction, SurfaceControl.Transaction tx, Rect bounds) {
+    private PictureInPictureSurfaceTransaction onAnimationScale(
+            float fraction, SurfaceControl.Transaction tx, Rect bounds) {
         if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
             final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
-            mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
+            return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
                     rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
         } else {
-            mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
+            return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
         }
     }
 
     /** scale and crop the window with source rect hint */
-    private void onAnimationScaleAndCrop(float fraction, SurfaceControl.Transaction tx,
+    private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop(
+            float fraction, SurfaceControl.Transaction tx,
             Rect bounds) {
         final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
                 mSourceHintRectInsets);
         if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
             final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
-            mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+            return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
                     rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
         } else {
-            mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+            return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
         }
     }
 
@@ -224,34 +232,12 @@
         return mDestinationBounds;
     }
 
-    /**
-     * @return {@link Rect} of the final window crop in destination orientation.
-     */
-    public Rect getFinishWindowCrop() {
-        final Rect windowCrop = new Rect(mAppBounds);
-        if (mSourceHintRectInsets != null) {
-            windowCrop.inset(mSourceHintRectInsets);
-        }
-        return windowCrop;
-    }
-
-    /**
-     * @return Array of 9 floats represents the final transform in destination orientation.
-     */
-    public float[] getFinishTransform() {
-        final Matrix transform = new Matrix();
-        final float[] float9 = new float[9];
-        if (mSourceHintRectInsets == null) {
-            transform.setRectToRect(new RectF(mAppBounds), new RectF(mDestinationBounds),
-                    Matrix.ScaleToFit.FILL);
-        } else {
-            final float scale = mAppBounds.width() <= mAppBounds.height()
-                    ? (float) mDestinationBounds.width() / mAppBounds.width()
-                    : (float) mDestinationBounds.height() / mAppBounds.height();
-            transform.setScale(scale, scale);
-        }
-        transform.getValues(float9);
-        return float9;
+    /** @return {@link PictureInPictureSurfaceTransaction} for the final leash transaction. */
+    public PictureInPictureSurfaceTransaction getFinishTransaction() {
+        // get the final leash operations but do not apply to the leash.
+        final SurfaceControl.Transaction tx =
+                PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+        return onAnimationUpdate(tx, FRACTION_END);
     }
 
     private RotatedPosition getRotatedPosition(float fraction) {
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 09bdaaf..d50115b 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,3 +1,6 @@
 <resources>
     <bool name="allow_rotation">true</bool>
+
+    <!-- Hotseat -->
+    <bool name="hotseat_transpose_layout_with_orientation">false</bool>
 </resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index ec07591..33fc553 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -3,7 +3,4 @@
 <!-- All Apps & Widgets -->
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
     <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
-
-<!-- Hotseat -->
-    <bool name="hotseat_transpose_layout_with_orientation">false</bool>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fc2b4bc..99f6c75 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -132,6 +132,9 @@
         <attr name="isScalable" format="boolean" />
         <attr name="devicePaddingId" format="reference" />
 
+        <!-- whether the grid option is shown to the user -->
+        <attr name="visible" format="boolean" />
+
     </declare-styleable>
 
     <declare-styleable name="DevicePadding">
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index e38ab74..9b350a1 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -329,6 +329,6 @@
      * views
      */
     public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) {
-        return new DefaultSearchAdapterProvider(this);
+        return new DefaultSearchAdapterProvider(this, allapps);
     }
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9df8d44..7ece72e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -19,7 +19,6 @@
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -602,7 +601,7 @@
         if (child instanceof BubbleTextView) {
             BubbleTextView bubbleChild = (BubbleTextView) child;
             bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
-            if (ENABLE_FOUR_COLUMNS.get()) {
+            if (mActivity.getDeviceProfile().isScalableGrid) {
                 bubbleChild.setCenterVertically(mContainerType != HOTSEAT);
             }
         }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 4049ed6..15e12d6 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -29,6 +29,7 @@
 
 import androidx.annotation.Nullable;
 
+import java.util.Arrays;
 import java.util.function.Consumer;
 
 /**
@@ -226,7 +227,7 @@
      * Returns the first View for which the given itemOperator returns true, or null.
      */
     public View getFirstItemMatch(Workspace.ItemOperator itemOperator) {
-        return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator);
+        return mWorkspace.getFirstMatch(Arrays.asList(this), itemOperator);
     }
 
     /**
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index b0c3bb4..754e988 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -626,6 +626,8 @@
         private final boolean isScalable;
         private final int devicePaddingId;
 
+        public final boolean visible;
+
         private final SparseArray<TypedValue> extraAttrs;
 
         public GridOption(Context context, AttributeSet attrs) {
@@ -652,6 +654,8 @@
             devicePaddingId = a.getResourceId(
                     R.styleable.GridDisplayOption_devicePaddingId, 0);
 
+            visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true);
+
             a.recycle();
 
             extraAttrs = Themes.createValueMap(context, attrs,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8785fbc..5eba399 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1781,9 +1781,7 @@
     @Override
     public Rect getFolderBoundingBox() {
         // We need to bound the folder to the currently visible workspace area
-        Rect folderBoundingBox = new Rect();
-        getWorkspace().getPageAreaRelativeToDragLayer(folderBoundingBox);
-        return folderBoundingBox;
+        return getWorkspace().getPageAreaRelativeToDragLayer();
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 57d7600..ccc023a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,8 +17,11 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
-import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,10 +40,10 @@
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
@@ -122,6 +125,34 @@
         mContext = context;
 
         mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
+
+        // b/175329686 Temporary logic to gracefully migrate group of users to the new 4x5 grid.
+        String gridName = InvariantDeviceProfile.getCurrentGridName(context);
+        if (ENABLE_FOUR_COLUMNS.get()
+                || "reasonable".equals(gridName)
+                || ENABLE_FOUR_COLUMNS.key.equals(gridName)) {
+            // Reset flag and remove it from developer options to prevent it from being enabled
+            // again.
+            ENABLE_FOUR_COLUMNS.reset(context);
+            FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS);
+
+            // Force migration code to run
+            Utilities.getPrefs(context).edit()
+                    .remove(KEY_MIGRATION_SRC_HOTSEAT_COUNT)
+                    .remove(KEY_MIGRATION_SRC_WORKSPACE_SIZE)
+                    .apply();
+
+            // We make an empty call here to ensure the database is created with the old IDP grid,
+            // so that when we set the new grid the migration can proceeds as expected.
+            LauncherSettings.Settings.call(context.getContentResolver(), "");
+
+            String newGridName = "practical";
+            Utilities.getPrefs(mContext).edit().putString("idp_grid_name", newGridName).commit();
+            mInvariantDeviceProfile.setCurrentGrid(context, "practical");
+        } else {
+            FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS);
+        }
+
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 25afb55..6c0daa4 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -15,6 +15,7 @@
     private static final String XML = ".xml";
 
     public static final String LAUNCHER_DB = "launcher.db";
+    public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db";
     public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
     public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
     public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
@@ -30,6 +31,7 @@
 
     public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
             LAUNCHER_DB,
+            LAUNCHER_4_BY_5_DB,
             LAUNCHER_4_BY_4_DB,
             LAUNCHER_3_BY_3_DB,
             LAUNCHER_2_BY_2_DB,
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 46bce93..1003958 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -17,9 +17,11 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
@@ -89,7 +91,7 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[9];
+    private static final LauncherState[] sAllStates = new LauncherState[10];
 
     /**
      * TODO: Create a separate class for NORMAL state.
@@ -112,6 +114,8 @@
             SPRING_LOADED_STATE_ORDINAL);
     public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
     public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
+    public static final LauncherState HINT_STATE_TWO_BUTTON = new HintState(
+            HINT_STATE_TWO_BUTTON_ORDINAL, LAUNCHER_STATE_OVERVIEW);
 
     public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
     public static final LauncherState OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 2f9c96e..f43452c 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -541,20 +541,17 @@
             c.setBitmap(preview);
             c.drawColor(0, PorterDuff.Mode.CLEAR);
         }
-        RectF boxRect = drawBoxWithShadow(c, size, size);
+
+        drawBoxWithShadow(c, size, size);
 
         LauncherIcons li = LauncherIcons.obtain(mContext);
-        Bitmap icon = li.createBadgedIconBitmap(
+        Drawable icon = li.createBadgedIconBitmap(
                 mutateOnMainThread(info.getFullResIcon(mIconCache)),
-                Process.myUserHandle(), 0).icon;
+                Process.myUserHandle(), 0).newIcon(launcher);
         li.recycle();
 
-        Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
-
-        boxRect.set(0, 0, iconSize, iconSize);
-        boxRect.offset(padding, padding);
-        c.drawBitmap(icon, src, boxRect,
-                new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
+        icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
+        icon.draw(c);
         c.setBitmap(null);
         return preview;
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index bd17348..981cf89 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2000,16 +2000,26 @@
     }
 
     /**
-     * Computes the area relative to dragLayer which is used to display a page.
+     * Computes and returns the area relative to dragLayer which is used to display a page.
+     * In case we have multiple pages displayed at the same time, we return the union of the areas.
      */
-    public void getPageAreaRelativeToDragLayer(Rect outArea) {
-        CellLayout child = (CellLayout) getChildAt(getNextPage());
-        if (child == null) {
-            return;
+    public Rect getPageAreaRelativeToDragLayer() {
+        Rect area = new Rect();
+        int nextPage = getNextPage();
+        int panelCount = getPanelCount();
+        for (int page = nextPage; page < nextPage + panelCount; page++) {
+            CellLayout child = (CellLayout) getChildAt(page);
+            if (child == null) {
+                break;
+            }
+
+            ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
+            Rect tmpRect = new Rect();
+            mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, tmpRect);
+            area.union(tmpRect);
         }
 
-        ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
-        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
+        return area;
     }
 
     @Override
@@ -2968,8 +2978,10 @@
      * @param user The user of the app to match.
      */
     public View getFirstMatchForAppClose(String packageName, UserHandle user) {
-        final int curPage = getCurrentPage();
-        final CellLayout currentPage = (CellLayout) getPageAt(curPage);
+        List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
+        cellLayouts.add(getHotseat());
+        getVisiblePages().forEach(page -> cellLayouts.add((CellLayout) page));
+
         final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
                 && info.getTargetComponent() != null
                 && TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
@@ -2990,13 +3002,11 @@
 
         // Order: App icons, app in folder. Items in hotseat get returned first.
         if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
-            return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
-                    packageAndUserAndApp, packageAndUserAndAppInFolder);
+            return getFirstMatch(cellLayouts, packageAndUserAndApp, packageAndUserAndAppInFolder);
         } else {
             // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
             // FolderAdaptiveIcon as the background.
-            return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
-                    packageAndUserAndApp);
+            return getFirstMatch(cellLayouts, packageAndUserAndApp);
         }
     }
 
@@ -3030,7 +3040,7 @@
      * @param operators List of operators, in order starting from best matching operator.
      * @return
      */
-    View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
+    View getFirstMatch(Iterable<CellLayout> cellLayouts, final ItemOperator... operators) {
         // This array is filled with the first match for each operator.
         final View[] matches = new View[operators.length];
         // For efficiency, the outer loop should be CellLayout.
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 16e022c..e0a4d4a 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -105,47 +105,39 @@
         int elements = state.getVisibleElements(mLauncher);
         Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
-        boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
         Hotseat hotseat = mWorkspace.getHotseat();
-        if (playAtomicComponent) {
-            Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
-            LauncherState fromState = mLauncher.getStateManager().getState();
-            boolean shouldSpring = propertySetter instanceof PendingAnimation
-                    && fromState == HINT_STATE && state == NORMAL;
-            if (shouldSpring) {
-                ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
-                        mWorkspace, mNewScale));
-            } else {
-                propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
-            }
+        Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
+        LauncherState fromState = mLauncher.getStateManager().getState();
 
-            setPivotToScaleWithWorkspace(hotseat);
-            float hotseatScale = hotseatScaleAndTranslation.scale;
-            if (shouldSpring) {
-                PendingAnimation pa = (PendingAnimation) propertySetter;
-                pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
-            } else {
-                Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
-                        scaleInterpolator);
-                propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
-                        hotseatScaleInterpolator);
-            }
-
-            float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-            propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
-            float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
-            propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
-                    workspacePageIndicatorAlpha, fadeInterpolator);
+        boolean shouldSpring = propertySetter instanceof PendingAnimation
+                && fromState == HINT_STATE && state == NORMAL;
+        if (shouldSpring) {
+            ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
+                    mWorkspace, mNewScale));
+        } else {
+            propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
         }
 
-        if (config.onlyPlayAtomicComponent()) {
-            // Only the alpha and scale, handled above, are included in the atomic animation.
-            return;
+        setPivotToScaleWithWorkspace(hotseat);
+        float hotseatScale = hotseatScaleAndTranslation.scale;
+        if (shouldSpring) {
+            PendingAnimation pa = (PendingAnimation) propertySetter;
+            pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+        } else {
+            Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
+                    scaleInterpolator);
+            propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+                    hotseatScaleInterpolator);
         }
 
-        Interpolator translationInterpolator = !playAtomicComponent
-                ? LINEAR
-                : config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
+        float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
+        propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
+        float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
+        propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+                workspacePageIndicatorAlpha, fadeInterpolator);
+
+        Interpolator translationInterpolator =
+                config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
                 scaleAndTranslation.translationX, translationInterpolator);
         propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
@@ -195,17 +187,12 @@
         int drawableAlpha = state.hasFlag(FLAG_WORKSPACE_HAS_BACKGROUNDS)
                 ? Math.round(pageAlpha * 255) : 0;
 
-        if (!config.onlyPlayAtomicComponent()) {
-            // Don't update the scrim during the atomic animation.
-            propertySetter.setInt(cl.getScrimBackground(),
-                    DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
-        }
-        if (config.playAtomicOverviewScaleComponent()) {
-            Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
-                    pageAlphaProvider.interpolator);
-            propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
-                    pageAlpha, fadeInterpolator);
-        }
+        propertySetter.setInt(cl.getScrimBackground(),
+                DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
+        Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
+                pageAlphaProvider.interpolator);
+        propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
+                pageAlpha, fadeInterpolator);
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index bf0a88f..225e1c1 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -77,7 +77,7 @@
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
         Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
 
-    private static final float FLING_VELOCITY_MULTIPLIER = 135f;
+    private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .8f;
     // Starts the springs after at least 55% of the animation has passed.
     private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
     private static final int ALPHA_CHANNEL_COUNT = 2;
@@ -140,12 +140,7 @@
 
         mAllAppsStore.addUpdateListener(this::onAppsUpdated);
 
-        addSpringView(R.id.all_apps_header);
-        addSpringView(R.id.apps_list_view);
-        addSpringView(R.id.all_apps_tabs_view_pager);
-
         mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
-
     }
 
     /**
@@ -169,17 +164,10 @@
         return mWorkModeSwitch;
     }
 
-
-    @Override
-    protected void setDampedScrollShift(float shift) {
-        // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
-        float maxShift = getSearchView().getHeight() / 2f;
-        super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift));
-    }
-
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
+            holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns);
             if (holder.recyclerView != null) {
                 // Remove all views and clear the pool, while keeping the data same. After this
                 // call, all the viewHolders will be recreated.
@@ -407,12 +395,6 @@
         }
     }
 
-    @Override
-    public int getCanvasClipTopForOverscroll() {
-        // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
-        return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
-    }
-
     private void rebindAdapters(boolean showTabs) {
         rebindAdapters(showTabs, false /* force */);
     }
@@ -639,11 +621,8 @@
             public void onAnimationUpdate(ValueAnimator valueAnimator) {
                 if (shouldSpring
                         && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
-                    int searchViewId = getSearchView().getId();
-                    addSpringView(searchViewId);
-                    finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
-                            (anim, canceled, value, velocity) -> removeSpringView(searchViewId));
-
+                    absorbSwipeUpVelocity(Math.abs(
+                            Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
                     shouldSpring = false;
                 }
             }
@@ -699,8 +678,7 @@
             applyPadding();
             setupOverlay();
             if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                recyclerView.addItemDecoration(new AllAppsSectionDecorator(
-                        AllAppsContainerView.this));
+                recyclerView.addItemDecoration(mSearchAdapterProvider.getDecorator());
             }
         }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index bb175ea..5b4c4c5 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -42,7 +42,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -108,7 +107,7 @@
         // The index of this app not including sections
         public int appIndex = -1;
         // Search section associated to result
-        public SectionDecorationInfo sectionDecorationInfo = null;
+        public DecorationInfo decorationInfo = null;
 
         /**
          * Factory method for AppIcon AdapterItem
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
deleted file mode 100644
index 0bd2f44..0000000
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2020 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.allapps;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.view.View;
-
-import androidx.annotation.Nullable;
-import androidx.core.graphics.ColorUtils;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
-import com.android.launcher3.util.Themes;
-
-import java.util.List;
-
-/**
- * ItemDecoration class that groups items in {@link AllAppsRecyclerView}
- */
-public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
-
-    private final AllAppsContainerView mAppsView;
-
-    AllAppsSectionDecorator(AllAppsContainerView appsContainerView) {
-        mAppsView = appsContainerView;
-    }
-
-    @Override
-    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
-        SearchAdapterProvider adapterProvider = mAppsView.getSearchAdapterProvider();
-        for (int i = 0; i < parent.getChildCount(); i++) {
-            View view = parent.getChildAt(i);
-            int position = parent.getChildAdapterPosition(view);
-            AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
-            if (adapterItem.sectionDecorationInfo != null) {
-                SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
-                SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
-                if (decorationHandler != null) {
-                    if (view.equals(adapterProvider.getHighlightedItem())) {
-                        decorationHandler.onFocusDraw(c, view);
-                    } else {
-                        decorationHandler.onGroupDraw(c, view);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Handles grouping and drawing of items in the same all apps sections.
-     */
-    public static class SectionDecorationHandler {
-        protected RectF mBounds = new RectF();
-        private final boolean mIsFullWidth;
-        private final float mRadius;
-
-        protected final int mFocusColor; // main focused item color
-        protected final int mFillcolor; // grouping color
-
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        private final boolean mIsTopRound;
-        private final boolean mIsBottomRound;
-        private float[] mCorners;
-        private float mFillSpacing;
-
-        public SectionDecorationHandler(Context context, boolean isFullWidth, int fillAlpha,
-                boolean isTopRound, boolean isBottomRound) {
-
-            mIsFullWidth = isFullWidth;
-            int endScrim = Themes.getColorBackground(context);
-            mFillcolor = ColorUtils.setAlphaComponent(endScrim, fillAlpha);
-            mFocusColor = endScrim;
-
-            mIsTopRound = isTopRound;
-            mIsBottomRound = isBottomRound;
-
-            mRadius = context.getResources().getDimensionPixelSize(
-                    R.dimen.search_decoration_corner_radius);
-            mFillSpacing = context.getResources().getDimensionPixelSize(
-                    R.dimen.search_decoration_padding);
-            mCorners = new float[]{
-                    mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top left radius in px
-                    mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top right radius in px
-                    mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0, // Bottom right
-                    mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0  // Bottom left
-            };
-
-        }
-
-        /**
-         * Draw bounds onto canvas.
-         */
-        public void onGroupDraw(Canvas canvas, View view) {
-            if (view == null) return;
-            mPaint.setColor(mFillcolor);
-            mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
-            onDraw(canvas);
-        }
-
-        /**
-         * Draw the bound of the view to the canvas.
-         */
-        public void onFocusDraw(Canvas canvas, @Nullable View view) {
-            if (view == null) {
-                return;
-            }
-            mPaint.setColor(mFocusColor);
-            mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
-            onDraw(canvas);
-        }
-
-
-        private void onDraw(Canvas canvas) {
-            final Path path = new Path();
-            RectF finalBounds = new RectF(mBounds.left + mFillSpacing,
-                    mBounds.top + mFillSpacing,
-                    mBounds.right - mFillSpacing,
-                    mBounds.bottom - mFillSpacing);
-            path.addRoundRect(finalBounds, mCorners, Path.Direction.CW);
-            canvas.drawPath(path, mPaint);
-        }
-
-        /**
-         * Reset view bounds to empty.
-         */
-        public void reset() {
-            mBounds.setEmpty();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 16ecd58..8c5b0fe 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -155,19 +155,12 @@
             StateAnimationConfig config, PendingAnimation builder) {
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
-            if (!config.onlyPlayAtomicComponent()) {
-                setAlphas(toState, config, builder);
-            }
+            setAlphas(toState, config, builder);
             // Fail fast
             onProgressAnimationEnd();
             return;
         }
 
-        if (config.onlyPlayAtomicComponent()) {
-            // There is no atomic component for the all apps transition, so just return early.
-            return;
-        }
-
         Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
                 ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
                 : FAST_OUT_SLOW_IN;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index fefd97a..6957850 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -20,7 +20,6 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -288,11 +287,6 @@
         mFastScrollerSections.clear();
         mAdapterItems.clear();
 
-        SectionDecorationInfo appSection = new SectionDecorationInfo();
-        appSection.setDecorationHandler(
-                new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true,
-                        0, false, false));
-
         // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
         // ordered set of sections
 
@@ -313,9 +307,7 @@
                 if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
                     lastFastScrollerSectionInfo.fastScrollToItem = appItem;
                 }
-                if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-                    appItem.sectionDecorationInfo = appSection;
-                }
+
                 mAdapterItems.add(appItem);
             }
         } else {
diff --git a/src/com/android/launcher3/allapps/DecorationInfo.java b/src/com/android/launcher3/allapps/DecorationInfo.java
new file mode 100644
index 0000000..50b250c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/DecorationInfo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.allapps;
+
+public class DecorationInfo {
+}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 0a2dea9..941d3af 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -49,11 +49,6 @@
     float getScrollRangeDelta(Rect insets);
 
     /**
-     * Called when activity is destroyed. Used to close search system services
-     */
-    default void destroySearch() { }
-
-    /**
      * @return the edit text object
      */
     @Nullable
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index bfcc1c7..c51bcf5 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -36,7 +36,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Insettable;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
@@ -136,7 +135,7 @@
         mApps = appsView.getApps();
         mAppsView = appsView;
         mSearchBarController.initialize(
-                new DefaultAppSearchAlgorithm(mLauncher, LauncherAppState.getInstance(mLauncher)),
+                new DefaultAppSearchAlgorithm(mLauncher),
                 this, mLauncher, this);
     }
 
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
deleted file mode 100644
index 34895ed..0000000
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2020 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.allapps.search;
-
-import android.content.Context;
-import android.os.CancellationSignal;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
-import com.android.launcher3.model.AllAppsList;
-import com.android.launcher3.model.BaseModelUpdateTask;
-import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.search.StringMatcherUtility;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * A device search section for handling app searches
- */
-public class AppsSearchPipeline implements SearchPipeline {
-
-    private static final int MAX_RESULTS_COUNT = 5;
-
-    private final SectionDecorationInfo mSearchSectionInfo;
-    private final LauncherAppState mLauncherAppState;
-
-    public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) {
-        mLauncherAppState = launcherAppState;
-        mSearchSectionInfo = new SectionDecorationInfo();
-        mSearchSectionInfo.setDecorationHandler(
-                new SectionDecorationHandler(context, true, 0, true, true));
-    }
-
-    @Override
-    public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
-            CancellationSignal cancellationSignal) {
-        mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
-            @Override
-            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
-                callback.accept(getAdapterItems(matchingResults));
-            }
-        });
-    }
-
-    /**
-     * Filters {@link AppInfo}s matching specified query
-     */
-    public static ArrayList<AppInfo> getTitleMatchResult(List<AppInfo> apps, String query) {
-        // Do an intersection of the words in the query and each title, and filter out all the
-        // apps that don't match all of the words in the query.
-        final String queryTextLower = query.toLowerCase();
-        final ArrayList<AppInfo> result = new ArrayList<>();
-        StringMatcherUtility.StringMatcher matcher =
-                StringMatcherUtility.StringMatcher.getInstance();
-        for (AppInfo info : apps) {
-            if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
-                result.add(info);
-            }
-        }
-        return result;
-    }
-
-    private ArrayList<AdapterItem> getAdapterItems(List<AppInfo> matchingApps) {
-        ArrayList<AdapterItem> items = new ArrayList<>();
-        for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) {
-            AdapterItem appItem = AdapterItem.asApp(i, "", matchingApps.get(i), i);
-            appItem.sectionDecorationInfo = mSearchSectionInfo;
-            items.add(appItem);
-        }
-
-        return items;
-    }
-}
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index a386ef8..1f854c6 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -15,25 +15,39 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
 import android.content.Context;
 import android.os.Handler;
 
+import androidx.annotation.AnyThread;
+
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.search.StringMatcherUtility;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The default search implementation.
  */
 public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> {
 
-    protected final Handler mResultHandler;
-    private final AppsSearchPipeline mAppsSearchPipeline;
+    private static final int MAX_RESULTS_COUNT = 5;
 
-    public DefaultAppSearchAlgorithm(Context context, LauncherAppState launcherAppState) {
-        mResultHandler = new Handler();
-        mAppsSearchPipeline = new AppsSearchPipeline(context, launcherAppState);
+    private final LauncherAppState mAppState;
+    private final Handler mResultHandler;
+
+    public DefaultAppSearchAlgorithm(Context context) {
+        mAppState = LauncherAppState.getInstance(context);
+        mResultHandler = new Handler(MAIN_EXECUTOR.getLooper());
     }
 
     @Override
@@ -44,11 +58,38 @@
     }
 
     @Override
-    public void doSearch(final String query,
-            final SearchCallback<AdapterItem> callback) {
-        mAppsSearchPipeline.query(query,
-                results -> mResultHandler.post(
-                        () -> callback.onSearchResult(query, results)),
-                null);
+    public void doSearch(String query, SearchCallback<AdapterItem> callback) {
+        mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                ArrayList<AdapterItem> result = getTitleMatchResult(apps.data, query);
+                mResultHandler.post(() -> callback.onSearchResult(query, result));
+            }
+        });
+    }
+
+    /**
+     * Filters {@link AppInfo}s matching specified query
+     */
+    @AnyThread
+    public static ArrayList<AdapterItem> getTitleMatchResult(List<AppInfo> apps, String query) {
+        // Do an intersection of the words in the query and each title, and filter out all the
+        // apps that don't match all of the words in the query.
+        final String queryTextLower = query.toLowerCase();
+        final ArrayList<AdapterItem> result = new ArrayList<>();
+        StringMatcherUtility.StringMatcher matcher =
+                StringMatcherUtility.StringMatcher.getInstance();
+
+        int resultCount = 0;
+        int total = apps.size();
+        for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
+            AppInfo info = apps.get(i);
+            if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
+                AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount);
+                result.add(appItem);
+                resultCount++;
+            }
+        }
+        return result;
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index ba895ed..ef62da4 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -15,12 +15,17 @@
  */
 package com.android.launcher3.allapps.search;
 
+import android.graphics.Canvas;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.launcher3.model.data.ItemInfo;
 
@@ -29,10 +34,19 @@
  */
 public class DefaultSearchAdapterProvider extends SearchAdapterProvider {
 
+    private final RecyclerView.ItemDecoration mDecoration;
     private View mHighlightedView;
 
-    public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) {
-        super(launcher);
+    public DefaultSearchAdapterProvider(BaseDraggingActivity launcher,
+            AllAppsContainerView appsContainerView) {
+        super(launcher, appsContainerView);
+        mDecoration = new RecyclerView.ItemDecoration() {
+            @Override
+            public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent,
+                    @NonNull RecyclerView.State state) {
+                super.onDraw(c, parent, state);
+            }
+        };
     }
 
     @Override
@@ -67,4 +81,9 @@
     public View getHighlightedItem() {
         return mHighlightedView;
     }
+
+    @Override
+    public RecyclerView.ItemDecoration getDecorator() {
+        return mDecoration;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index a650a7d..cefb8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -21,7 +21,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 
 /**
@@ -31,7 +34,7 @@
 
     protected final BaseDraggingActivity mLauncher;
 
-    public SearchAdapterProvider(BaseDraggingActivity launcher) {
+    public SearchAdapterProvider(BaseDraggingActivity launcher, AllAppsContainerView appsView) {
         mLauncher = launcher;
     }
 
@@ -72,7 +75,7 @@
     }
 
     /**
-     * handles selection event on search adapter item. Returns false if provider can not handle
+     * Handles selection event on search adapter item. Returns false if provider can not handle
      * event
      */
     public abstract boolean launchHighlightedItem();
@@ -82,5 +85,8 @@
      */
     public abstract View getHighlightedItem();
 
-
+    /**
+     * Returns the item decorator.
+     */
+    public abstract RecyclerView.ItemDecoration getDecorator();
 }
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
deleted file mode 100644
index 3516a41..0000000
--- a/src/com/android/launcher3/allapps/search/SearchPipeline.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 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.allapps.search;
-
-import android.os.CancellationSignal;
-
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * An interface for handling search within pipeline
- */
-// Remove when System Service API is added.
-public interface SearchPipeline {
-
-    /**
-     * Perform query
-     */
-    void query(String input,
-            Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
-            CancellationSignal cancellationSignal);
-}
diff --git a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
deleted file mode 100644
index 56dd63c..0000000
--- a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2020 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.allapps.search;
-
-import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
-
-/**
- * Info class for a search section that is primarily used for decoration.
- */
-public class SectionDecorationInfo {
-    public static final int GROUPING = 1 << 1;
-
-    private String mSectionId;
-    private SectionDecorationHandler mDecorationHandler;
-
-    public SectionDecorationInfo() {
-        this(null);
-    }
-
-    public SectionDecorationInfo(String sectionId) {
-        mSectionId = sectionId;
-    }
-
-    public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
-        mDecorationHandler = sectionDecorationHandler;
-    }
-
-    public SectionDecorationHandler getDecorationHandler() {
-        return mDecorationHandler;
-    }
-
-    /**
-     * Returns the section's ID
-     */
-    public String getSectionId() {
-        return mSectionId == null ? "" : mSectionId;
-    }
-}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 03c802a..c7f0133 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -187,7 +187,7 @@
             "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
               + "Any apps occupying the first row will be removed from workspace.");
 
-    public static final BooleanFlag ENABLE_FOUR_COLUMNS = new DeviceFlag(
+    public static final DeviceFlag ENABLE_FOUR_COLUMNS = new DeviceFlag(
             "ENABLE_FOUR_COLUMNS", false, "Uses 4 columns in launcher grid."
             + "Warning: This will permanently alter your home screen items and is not reversible.");
 
@@ -227,6 +227,12 @@
         }
     }
 
+    public static void removeFlag(DebugFlag flag) {
+        synchronized (sDebugFlags) {
+            sDebugFlags.remove(flag);
+        }
+    }
+
     static List<DebugFlag> getDebugFlags() {
         synchronized (sDebugFlags) {
             return new ArrayList<>(sDebugFlags);
@@ -304,6 +310,15 @@
                     .getBoolean(key, defaultValue);
         }
 
+        /**
+         * Resets value to default value.
+         */
+        public void reset(Context context) {
+            mCurrentValue = defaultValue;
+            context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+                    .edit().putBoolean(key, defaultValue).apply();
+        }
+
         @Override
         protected StringBuilder appendProps(StringBuilder src) {
             return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue);
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index cb42e7a..911f8c3 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -90,7 +90,10 @@
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
                 if ((type == XmlPullParser.START_TAG)
                         && GridOption.TAG_NAME.equals(parser.getName())) {
-                    result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
+                    GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser));
+                    if (option.visible) {
+                        result.add(option);
+                    }
                 }
             }
         } catch (IOException | XmlPullParserException e) {
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 80eeb22..d27d8c7 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -22,7 +22,6 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -86,9 +85,8 @@
             mIsIconLarge = true;
         }
         if (mIconDrawable == null) {
-            mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
-                    .getInstance(context).getIconCache()
-                    .getDefaultIcon(statusBarNotification.getUser()).icon);
+            mIconDrawable = LauncherAppState.getInstance(context).getIconCache()
+                    .getDefaultIcon(statusBarNotification.getUser()).newIcon(context);
         }
         intent = notification.contentIntent;
         autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0;
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 51767e7..14ef2dc 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -18,7 +18,7 @@
 
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
@@ -289,14 +289,14 @@
      */
     public AnimatorPlaybackController createAnimationToNewWorkspace(
             STATE_TYPE state, long duration) {
-        return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
+        return createAnimationToNewWorkspace(state, duration, 0 /* animFlags */);
     }
 
     public AnimatorPlaybackController createAnimationToNewWorkspace(
-            STATE_TYPE state, long duration, @AnimationFlags int animComponents) {
+            STATE_TYPE state, long duration, @AnimationFlags int animFlags) {
         StateAnimationConfig config = new StateAnimationConfig();
         config.duration = duration;
-        config.animFlags = animComponents;
+        config.animFlags = animFlags;
         return createAnimationToNewWorkspace(state, config);
     }
 
@@ -312,7 +312,7 @@
 
     private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
         PendingAnimation builder = new PendingAnimation(mConfig.duration);
-        if (mConfig.getAnimComponents() != 0) {
+        if (!mConfig.hasAnimationFlag(SKIP_ALL_ANIMATIONS)) {
             for (StateHandler handler : getStateHandlers()) {
                 handler.setStateWithAnimation(state, mConfig, builder);
             }
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index eb2c551..76f89bc 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -31,7 +31,11 @@
             | FLAG_HAS_SYS_UI_SCRIM;
 
     public HintState(int id) {
-        super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
+        this(id, LAUNCHER_STATE_HOME);
+    }
+
+    public HintState(int id, int statsLogOrdinal) {
+        super(id, statsLogOrdinal, STATE_FLAGS);
     }
 
     @Override
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index cd74390..8e7dcc0 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -27,32 +27,21 @@
  */
 public class StateAnimationConfig {
 
-    // We separate the state animations into "atomic" and "non-atomic" components. The atomic
-    // components may be run atomically - that is, all at once, instead of user-controlled. However,
-    // atomic components are not restricted to this purpose; they can be user-controlled alongside
-    // non atomic components as well. Note that each gesture model has exactly one atomic component,
-    // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
     @IntDef(flag = true, value = {
-            PLAY_NON_ATOMIC,
-            PLAY_ATOMIC_OVERVIEW_SCALE,
-            PLAY_ATOMIC_OVERVIEW_PEEK,
+            SKIP_ALL_ANIMATIONS,
             SKIP_OVERVIEW,
             SKIP_DEPTH_CONTROLLER,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationFlags {}
-    public static final int PLAY_NON_ATOMIC = 1 << 0;
-    public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
-    public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
-    public static final int SKIP_OVERVIEW = 1 << 3;
-    public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+    public static final int SKIP_ALL_ANIMATIONS = 1 << 0;
+    public static final int SKIP_OVERVIEW = 1 << 1;
+    public static final int SKIP_DEPTH_CONTROLLER = 1 << 2;
 
     public long duration;
     public boolean userControlled;
-    public @AnimationFlags int animFlags = ANIM_ALL_COMPONENTS;
+    public @AnimationFlags int animFlags = 0;
 
-    public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
-            | PLAY_ATOMIC_OVERVIEW_PEEK;
 
     // Various types of animation state transition
     @IntDef(value = {
@@ -127,37 +116,9 @@
     }
 
     /**
-     * @return Whether Overview is scaling as part of this animation. If this is the only
-     * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
-     * atomically, rather than being part of a normal state animation. StateHandlers can use
-     * this to designate part of their animation that should scale with Overview.
-     */
-    public boolean playAtomicOverviewScaleComponent() {
-        return hasAnimationFlag(StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE);
-    }
-
-    /**
-     * @return Whether this animation will play atomically at the same time as a different,
-     * user-controlled state transition. StateHandlers, which contribute to both animations, can
-     * use this to avoid animating the same properties in both animations, since they'd conflict
-     * with one another.
-     */
-    public boolean onlyPlayAtomicComponent() {
-        return getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE
-                || getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-    }
-
-    /**
      * Returns true if the config and any of the provided component flags
      */
     public boolean hasAnimationFlag(@AnimationFlags int a) {
         return (animFlags & a) != 0;
     }
-
-    /**
-     * @return Only the flags that determine which animation components to play.
-     */
-    public @AnimationFlags int getAnimComponents() {
-        return animFlags & StateAnimationConfig.ANIM_ALL_COMPONENTS;
-    }
 }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index f34bff6..943d129 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -32,7 +32,8 @@
     public static final int ALL_APPS_STATE_ORDINAL = 5;
     public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
     public static final int HINT_STATE_ORDINAL = 7;
-    public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 8;
+    public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8;
+    public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
     public static final String SEQUENCE_MAIN = "Main";
     public static final String SEQUENCE_TIS = "TIS";
@@ -56,6 +57,8 @@
                 return "Background";
             case HINT_STATE_ORDINAL:
                 return "Hint";
+            case HINT_STATE_TWO_BUTTON_ORDINAL:
+                return "Hint2Button";
             case OVERVIEW_SPLIT_SELECT_ORDINAL:
                 return "OverviewSplitSelect";
             default:
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 516fc74..2889801 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -27,30 +27,20 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
-import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.os.SystemClock;
-import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
 import com.android.launcher3.util.FlingBlockCheck;
 import com.android.launcher3.util.TouchController;
 
@@ -60,13 +50,6 @@
 public abstract class AbstractStateChangeTouchController
         implements TouchController, SingleAxisSwipeDetector.Listener {
 
-    /**
-     * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
-     * TODO: Remove the atomic animation altogether and just go to OVERVIEW directly (b/175137718).
-     */
-    public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 1f;
-    protected final long ATOMIC_DURATION = getAtomicDuration();
-
     protected final Launcher mLauncher;
     protected final SingleAxisSwipeDetector mDetector;
     protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
@@ -89,23 +72,7 @@
     private float mProgressMultiplier;
     private float mDisplacementShift;
     private boolean mCanBlockFling;
-    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
-    protected AnimatorSet mAtomicAnim;
-    // True if we want to resume playing atomic components when mAtomicAnim completes.
-    private boolean mScheduleResumeAtomicComponent;
-    private AutoPlayAtomicAnimationInfo mAtomicAnimAutoPlayInfo;
-
-    private boolean mPassedOverviewAtomicThreshold;
-    // mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
-    // However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
-    // atomic animation finishes, we only control the non-atomic components so that we don't
-    // interfere with the atomic animation. When the atomic animation ends, we start controlling
-    // the atomic components as well, using this controller.
-    private AnimatorPlaybackController mAtomicComponentsController;
-    private LauncherState mAtomicComponentsTargetState = NORMAL;
-
-    private float mAtomicComponentsStartProgress;
+    private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
 
     public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
         mLauncher = l;
@@ -113,10 +80,6 @@
         mSwipeDirection = dir;
     }
 
-    protected long getAtomicDuration() {
-        return 200;
-    }
-
     protected abstract boolean canInterceptTouch(MotionEvent ev);
 
     @Override
@@ -182,7 +145,7 @@
     protected abstract LauncherState getTargetState(LauncherState fromState,
             boolean isDragTowardPositive);
 
-    protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
+    protected abstract float initCurrentAnimation();
 
     private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
         LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -199,28 +162,10 @@
         mToState = newToState;
 
         mStartProgress = 0;
-        mPassedOverviewAtomicThreshold = false;
         if (mCurrentAnimation != null) {
             mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
         }
-        int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
-                ? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS;
-        mScheduleResumeAtomicComponent = false;
-        if (mAtomicAnim != null) {
-            animComponents = PLAY_NON_ATOMIC;
-            // Control the non-atomic components until the atomic animation finishes, then control
-            // the atomic components as well.
-            mScheduleResumeAtomicComponent = true;
-        }
-        if (goingBetweenNormalAndOverview(mFromState, mToState)
-                || mAtomicComponentsTargetState != mToState) {
-            cancelAtomicComponentsController();
-        }
-
-        if (mAtomicComponentsController != null) {
-            animComponents &= ~PLAY_ATOMIC_OVERVIEW_SCALE;
-        }
-        mProgressMultiplier = initCurrentAnimation(animComponents);
+        mProgressMultiplier = initCurrentAnimation();
         mCurrentAnimation.dispatchOnStart();
         return true;
     }
@@ -231,13 +176,6 @@
     protected void onReachedFinalState(LauncherState newToState) {
     }
 
-    protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
-            LauncherState toState) {
-        return (fromState == NORMAL || fromState == OVERVIEW)
-                && (toState == NORMAL || toState == OVERVIEW)
-                && mGoingBetweenStates;
-    }
-
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
         mStartState = mLauncher.getStateManager().getState();
@@ -252,11 +190,6 @@
         } else {
             mCurrentAnimation.pause();
             mStartProgress = mCurrentAnimation.getProgressFraction();
-
-            mAtomicAnimAutoPlayInfo = null;
-            if (mAtomicComponentsController != null) {
-                mAtomicComponentsController.pause();
-            }
         }
         mCanBlockFling = mFromState == NORMAL;
         mFlingBlockCheck.unblockFling();
@@ -310,69 +243,6 @@
             return;
         }
         mCurrentAnimation.setPlayFraction(fraction);
-        if (mAtomicComponentsController != null) {
-            // Make sure we don't divide by 0, and have at least a small runway.
-            float start = Math.min(mAtomicComponentsStartProgress, 0.9f);
-            mAtomicComponentsController.setPlayFraction((fraction - start) / (1 - start));
-        }
-        maybeUpdateAtomicAnim(mFromState, mToState, fraction);
-    }
-
-    /**
-     * When going between normal and overview states, see if we passed the overview threshold and
-     * play the appropriate atomic animation if so.
-     */
-    private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
-            float progress) {
-        if (!goingBetweenNormalAndOverview(fromState, toState)) {
-            return;
-        }
-        boolean passedThreshold = progress >= ATOMIC_OVERVIEW_ANIM_THRESHOLD;
-        if (passedThreshold != mPassedOverviewAtomicThreshold) {
-            LauncherState atomicFromState = passedThreshold ? fromState: toState;
-            LauncherState atomicToState = passedThreshold ? toState : fromState;
-            mPassedOverviewAtomicThreshold = passedThreshold;
-            if (mAtomicAnim != null) {
-                mAtomicAnim.cancel();
-            }
-            mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
-            mAtomicAnim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    mAtomicAnim = null;
-                    mScheduleResumeAtomicComponent = false;
-                }
-
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (!mScheduleResumeAtomicComponent) {
-                        return;
-                    }
-                    cancelAtomicComponentsController();
-
-                    if (mCurrentAnimation != null) {
-                        mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
-                        long duration = (long) (getShiftRange() * 2);
-                        mAtomicComponentsController = AnimatorPlaybackController.wrap(
-                                createAtomicAnimForState(mFromState, mToState, duration), duration);
-                        mAtomicComponentsController.dispatchOnStart();
-                        mAtomicComponentsTargetState = mToState;
-                        maybeAutoPlayAtomicComponentsAnim();
-                    }
-                }
-            });
-            mAtomicAnim.start();
-            mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-        }
-    }
-
-    private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
-            long duration) {
-        StateAnimationConfig config = getConfigForStates(fromState, targetState);
-        config.animFlags = PLAY_ATOMIC_OVERVIEW_SCALE;
-        config.duration = duration;
-        return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, config);
     }
 
     /**
@@ -451,59 +321,12 @@
         mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState));
         ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
         anim.setFloatValues(startProgress, endProgress);
-        maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
-        updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
-                targetState, velocity, fling);
+        updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
         if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
-        mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
-        maybeAutoPlayAtomicComponentsAnim();
-    }
-
-    /**
-     * Animates the atomic components from the current progress to the final progress.
-     *
-     * Note that this only applies when we are controlling the atomic components separately from
-     * the non-atomic components, which only happens if we reinit before the atomic animation
-     * finishes.
-     */
-    private void maybeAutoPlayAtomicComponentsAnim() {
-        if (mAtomicComponentsController == null || mAtomicAnimAutoPlayInfo == null) {
-            return;
-        }
-
-        final AnimatorPlaybackController controller = mAtomicComponentsController;
-        ValueAnimator atomicAnim = controller.getAnimationPlayer();
-        atomicAnim.setFloatValues(controller.getProgressFraction(),
-                mAtomicAnimAutoPlayInfo.toProgress);
-        long duration = mAtomicAnimAutoPlayInfo.endTime - SystemClock.elapsedRealtime();
-        mAtomicAnimAutoPlayInfo = null;
-        if (duration <= 0) {
-            atomicAnim.start();
-            atomicAnim.end();
-            mAtomicComponentsController = null;
-        } else {
-            atomicAnim.setDuration(duration);
-            atomicAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mAtomicComponentsController == controller) {
-                        mAtomicComponentsController = null;
-                    }
-                }
-            });
-            atomicAnim.start();
-        }
-    }
-
-    private long getRemainingAtomicDuration() {
-        if (mAtomicAnim == null) {
-            return 0;
-        }
-        return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime();
     }
 
     protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
@@ -513,10 +336,6 @@
     }
 
     protected void onSwipeInteractionCompleted(LauncherState targetState) {
-        if (mAtomicComponentsController != null) {
-            mAtomicComponentsController.getAnimationPlayer().end();
-            mAtomicComponentsController = null;
-        }
         onReachedFinalState(mToState);
         clearState();
         boolean shouldGoToTargetState = mGoingBetweenStates || (mToState != targetState);
@@ -556,37 +375,12 @@
 
     protected void clearState() {
         cancelAnimationControllers();
-        if (mAtomicAnim != null) {
-            mAtomicAnim.cancel();
-            mAtomicAnim = null;
-        }
         mGoingBetweenStates = true;
-        mScheduleResumeAtomicComponent = false;
         mDetector.finishedScrolling();
         mDetector.setDetectableScrollConditions(0, false);
     }
 
     private void cancelAnimationControllers() {
         mCurrentAnimation = null;
-        cancelAtomicComponentsController();
-    }
-
-    private void cancelAtomicComponentsController() {
-        if (mAtomicComponentsController != null) {
-            mAtomicComponentsController.getAnimationPlayer().cancel();
-            mAtomicComponentsController = null;
-        }
-        mAtomicAnimAutoPlayInfo = null;
-    }
-
-    private static class AutoPlayAtomicAnimationInfo {
-
-        public final float toProgress;
-        public final long endTime;
-
-        AutoPlayAtomicAnimationInfo(float toProgress, long duration) {
-            this.toProgress = toProgress;
-            this.endTime = duration + SystemClock.elapsedRealtime();
-        }
     }
 }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index f9dcf2d..ab2652a 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,24 +23,18 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
 
 /**
  * TouchController to switch between NORMAL and ALL_APPS state.
  */
 public class AllAppsSwipeController extends AbstractStateChangeTouchController {
 
-    private MotionEvent mTouchDownEvent;
-
     public AllAppsSwipeController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
     }
 
     @Override
     protected boolean canInterceptTouch(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mTouchDownEvent = ev;
-        }
         if (mCurrentAnimation != null) {
             // If we are already animating from a previous state, we can intercept.
             return true;
@@ -69,11 +63,11 @@
     }
 
     @Override
-    protected float initCurrentAnimation(@AnimationFlags int animComponents) {
+    protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
         mCurrentAnimation = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(mToState, maxAccuracy, animComponents);
+                .createAnimationToNewWorkspace(mToState, maxAccuracy);
         float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
         float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
         float totalShift = endVerticalShift - startVerticalShift;
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index d0ec9d7..9701389 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -15,51 +15,25 @@
  */
 package com.android.launcher3.views;
 
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.util.SparseBooleanArray;
-import android.view.View;
 import android.widget.EdgeEffect;
 import android.widget.RelativeLayout;
 
 import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
 
+import com.android.launcher3.Utilities;
+
+/**
+ * View group to allow rendering overscroll effect in a child at the parent level
+ */
 public class SpringRelativeLayout extends RelativeLayout {
 
-    private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
-    private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
-    private static final float VELOCITY_MULTIPLIER = 0.3f;
-
-    private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
-            new FloatPropertyCompat<SpringRelativeLayout>("value") {
-
-                @Override
-                public float getValue(SpringRelativeLayout object) {
-                    return object.mDampedScrollShift;
-                }
-
-                @Override
-                public void setValue(SpringRelativeLayout object, float value) {
-                    object.setDampedScrollShift(value);
-                }
-            };
-
-    protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
-    private final SpringAnimation mSpring;
-
-    private float mDampedScrollShift = 0;
-    private SpringEdgeEffect mActiveEdge;
+    private final EdgeEffect mEdgeGlowTop;
+    private final EdgeEffect mEdgeGlowBottom;
 
     public SpringRelativeLayout(Context context) {
         this(context, null);
@@ -71,98 +45,73 @@
 
     public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
-        mSpring.setSpring(new SpringForce(0)
-                .setStiffness(STIFFNESS)
-                .setDampingRatio(DAMPING_RATIO));
-    }
-
-    public void addSpringView(int id) {
-        mSpringViews.put(id, true);
-    }
-
-    public void removeSpringView(int id) {
-        mSpringViews.delete(id);
-        invalidate();
-    }
-
-    /**
-     * Used to clip the canvas when drawing child views during overscroll.
-     */
-    public int getCanvasClipTopForOverscroll() {
-        return 0;
+        mEdgeGlowTop = Utilities.ATLEAST_S
+                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+        mEdgeGlowBottom = Utilities.ATLEAST_S
+                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+        setWillNotDraw(false);
     }
 
     @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
-            int saveCount = canvas.save();
-
-            canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
-            canvas.translate(0, mDampedScrollShift);
-            boolean result = super.drawChild(canvas, child, drawingTime);
-
-            canvas.restoreToCount(saveCount);
-
-            return result;
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (!mEdgeGlowTop.isFinished()) {
+            final int restoreCount = canvas.save();
+            canvas.translate(0, 0);
+            mEdgeGlowTop.setSize(getWidth(), getHeight());
+            if (mEdgeGlowTop.draw(canvas)) {
+                postInvalidateOnAnimation();
+            }
+            canvas.restoreToCount(restoreCount);
         }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    private void setActiveEdge(SpringEdgeEffect edge) {
-        if (mActiveEdge != edge && mActiveEdge != null) {
-            mActiveEdge.mDistance = 0;
-        }
-        mActiveEdge = edge;
-    }
-
-    protected void setDampedScrollShift(float shift) {
-        if (shift != mDampedScrollShift) {
-            mDampedScrollShift = shift;
-            invalidate();
+        if (!mEdgeGlowBottom.isFinished()) {
+            final int restoreCount = canvas.save();
+            final int width = getWidth();
+            final int height = getHeight();
+            canvas.translate(-width, height);
+            canvas.rotate(180, width, 0);
+            mEdgeGlowBottom.setSize(width, height);
+            if (mEdgeGlowBottom.draw(canvas)) {
+                postInvalidateOnAnimation();
+            }
+            canvas.restoreToCount(restoreCount);
         }
     }
 
-    private void finishScrollWithVelocity(float velocity) {
-        mSpring.setStartVelocity(velocity);
-        mSpring.setStartValue(mDampedScrollShift);
-        mSpring.start();
-    }
 
-    protected void finishWithShiftAndVelocity(float shift, float velocity,
-            DynamicAnimation.OnAnimationEndListener listener) {
-        setDampedScrollShift(shift);
-        mSpring.addEndListener(listener);
-        finishScrollWithVelocity(velocity);
+    /**
+     * Absorbs the velocity as a result for swipe-up fling
+     */
+    protected void absorbSwipeUpVelocity(int velocity) {
+        mEdgeGlowBottom.onAbsorb(velocity);
+        invalidate();
     }
 
     public EdgeEffectFactory createEdgeEffectFactory() {
-        return new SpringEdgeEffectFactory();
+        return new ProxyEdgeEffectFactory();
     }
 
-    private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+    private class ProxyEdgeEffectFactory extends EdgeEffectFactory {
 
         @NonNull @Override
         protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
             switch (direction) {
                 case DIRECTION_TOP:
-                    return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
+                    return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
                 case DIRECTION_BOTTOM:
-                    return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+                    return new EdgeEffectProxy(getContext(), mEdgeGlowBottom);
             }
             return super.createEdgeEffect(view, direction);
         }
     }
 
-    private class SpringEdgeEffect extends EdgeEffect {
+    private class EdgeEffectProxy extends EdgeEffect {
 
-        private final float mVelocityMultiplier;
+        private final EdgeEffect mParent;
 
-        private float mDistance;
-
-        public SpringEdgeEffect(Context context, float velocityMultiplier) {
+        EdgeEffectProxy(Context context, EdgeEffect parent) {
             super(context);
-            mVelocityMultiplier = velocityMultiplier;
+            mParent = parent;
         }
 
         @Override
@@ -170,22 +119,44 @@
             return false;
         }
 
+        private void invalidateParentScrollEffect() {
+            if (!mParent.isFinished()) {
+                invalidate();
+            }
+        }
+
         @Override
         public void onAbsorb(int velocity) {
-            finishScrollWithVelocity(velocity * mVelocityMultiplier);
+            mParent.onAbsorb(velocity);
+            invalidateParentScrollEffect();
+        }
+
+        @Override
+        public void onPull(float deltaDistance) {
+            mParent.onPull(deltaDistance);
+            invalidateParentScrollEffect();
         }
 
         @Override
         public void onPull(float deltaDistance, float displacement) {
-            setActiveEdge(this);
-            mDistance += deltaDistance * (mVelocityMultiplier / 3f);
-            setDampedScrollShift(mDistance * getHeight());
+            mParent.onPull(deltaDistance, displacement);
+            invalidateParentScrollEffect();
         }
 
         @Override
         public void onRelease() {
-            mDistance = 0;
-            finishScrollWithVelocity(0);
+            mParent.onRelease();
+            invalidateParentScrollEffect();
+        }
+
+        @Override
+        public void finish() {
+            mParent.finish();
+        }
+
+        @Override
+        public boolean isFinished() {
+            return mParent.isFinished();
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index ad0a401..3e61e56 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.RoundDrawableWrapper;
 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 
 /**
@@ -118,12 +119,12 @@
                         .addDragListener(new AppWidgetHostViewDragListener(launcher));
             }
             if (preview == null) {
-                FastBitmapDrawable p = new FastBitmapDrawable(
+                Drawable p = new FastBitmapDrawable(
                         app.getWidgetCache().generateWidgetPreview(launcher,
                                 createWidgetInfo.info, maxWidth, null,
                                 previewSizeBeforeScale).first);
                 if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
-                    p.setRoundedCornersRadius(mEnforcedRoundedCornersForWidget);
+                    p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
                 }
                 preview = p;
             }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index c11b68e..40b256b 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.RoundDrawableWrapper;
 import com.android.launcher3.model.WidgetItem;
 
 /**
@@ -248,8 +249,7 @@
 
     public void applyPreview(Bitmap bitmap) {
         FastBitmapDrawable drawable = new FastBitmapDrawable(bitmap);
-        drawable.setRoundedCornersRadius(mEnforcedCornerRadius);
-        applyPreview(drawable);
+        applyPreview(new RoundDrawableWrapper(drawable, mEnforcedCornerRadius));
     }
 
     private void applyPreview(Drawable drawable) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 29c00b2..d13884a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -144,17 +144,12 @@
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(1));
             fastScroller.setIsRecyclerViewFirstChildInParent(false);
-            springLayout.addSpringView(R.id.primary_widgets_list_view);
-            springLayout.addSpringView(R.id.work_widgets_list_view);
         } else {
             mViewPager = null;
-            springLayout.addSpringView(R.id.primary_widgets_list_view);
         }
 
         layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
                 true);
-        springLayout.addSpringView(R.id.search_and_recommendations_container);
-
         mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
                 findViewById(R.id.search_and_recommendations_container));
         mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 72b1d22..d4e8f1f 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -512,7 +512,7 @@
     }
 
     protected boolean isInBackground(Launcher launcher) {
-        return !launcher.hasBeenResumed();
+        return launcher == null || !launcher.hasBeenResumed();
     }
 
     protected boolean isInState(Supplier<LauncherState> state) {
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 919c89f..bfacc74 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -42,6 +42,8 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.List;
@@ -89,7 +91,8 @@
         });
     }
 
-//    @Test
+    @Ignore("b/182844465")
+    @Test
     public void workTabExists() {
         mDevice.pressHome();
         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -101,7 +104,8 @@
                 launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
     }
 
-//    @Test
+    @Ignore("b/182844465")
+    @Test
     public void toggleWorks() {
         mDevice.pressHome();
         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -132,7 +136,8 @@
                 l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
     }
 
-//    @Test
+    @Ignore("b/182844465")
+    @Test
     public void testWorkEduFlow() {
         mDevice.pressHome();
         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -175,7 +180,8 @@
         });
     }
 
-//    @Test
+    @Ignore("b/182844465")
+    @Test
     public void testWorkEduIntermittent() {
         mDevice.pressHome();
         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);