Merge "Calculate sizes for responsive grid" into udc-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 88fea31..0a83279 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -36,6 +36,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.util.Log;
 import android.util.Pair;
 import android.view.DragEvent;
 import android.view.MotionEvent;
@@ -87,6 +88,7 @@
  */
 public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
         TaskbarControllers.LoggableTaskbarController {
+    private static final String TAG = "TaskbarDragController";
 
     private static final boolean DEBUG_DRAG_SHADOW_SURFACE = false;
     private static final int ANIM_DURATION_RETURN_ICON_TO_TASKBAR = 300;
@@ -318,12 +320,26 @@
             @Override
             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
                 int iconSize = Math.max(mDragIconSize, btv.getWidth());
-                shadowSize.set(iconSize, iconSize);
+                if (iconSize > 0) {
+                    shadowSize.set(iconSize, iconSize);
+                } else {
+                    Log.d(TAG, "Invalid icon size, dragSize=" + mDragIconSize
+                            + " viewWidth=" + btv.getWidth());
+                }
+
                 // The registration point was taken before the icon scaled to mDragIconSize, so
                 // offset the registration to where the touch is on the new size.
                 int offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2;
                 int offsetY = (mDragIconSize - mDragObject.dragView.getDragRegionHeight()) / 2;
-                shadowTouchPoint.set(mRegistrationX + offsetX, mRegistrationY + offsetY);
+                int touchX = mRegistrationX + offsetX;
+                int touchY = mRegistrationY + offsetY;
+                if (touchX >= 0 && touchY >= 0) {
+                    shadowTouchPoint.set(touchX, touchY);
+                } else {
+                    Log.d(TAG, "Invalid touch point, "
+                            + "registrationXY=(" + mRegistrationX + ", " + mRegistrationY + ") "
+                            + "offsetXY=(" + offsetX + ", " + offsetY + ")");
+                }
             }
 
             @Override
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 2008129..4dbf4e3 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,6 +19,7 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
@@ -133,7 +134,8 @@
 
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
-        if (finalState == NORMAL || finalState == SPRING_LOADED || finalState == ALL_APPS) {
+        if (finalState == NORMAL || finalState == SPRING_LOADED  || finalState == EDIT_MODE
+                || finalState == ALL_APPS) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
         }
diff --git a/res/color-night-v31/folder_background_dark.xml b/res/color-night-v31/folder_background_dark.xml
index 696e8ea..f415210 100644
--- a/res/color-night-v31/folder_background_dark.xml
+++ b/res/color-night-v31/folder_background_dark.xml
@@ -15,6 +15,6 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
-        android:color="@android:color/system_neutral2_500"
-        android:lStar="35" />
+        android:color="@android:color/system_neutral2_900"
+        android:lStar="12" />
 </selector>
diff --git a/res/color-night-v31/folder_preview_dark.xml b/res/color-night-v31/folder_preview_dark.xml
index bdd48a2..644d61a 100644
--- a/res/color-night-v31/folder_preview_dark.xml
+++ b/res/color-night-v31/folder_preview_dark.xml
@@ -15,6 +15,6 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
-        android:color="@android:color/system_neutral2_500"
-        android:lStar="30" />
+        android:color="@android:color/system_neutral1_900"
+        android:lStar="17" />
 </selector>
diff --git a/res/color-v31/folder_background_light.xml b/res/color-v31/folder_background_light.xml
index eb2fdd7..4dd088b 100644
--- a/res/color-v31/folder_background_light.xml
+++ b/res/color-v31/folder_background_light.xml
@@ -15,6 +15,6 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
-        android:color="@android:color/system_neutral1_500"
-        android:lStar="98" />
+        android:color="@android:color/system_neutral1_50"
+        android:lStar="94" />
 </selector>
diff --git a/res/color-v31/folder_preview_light.xml b/res/color-v31/folder_preview_light.xml
index ed1205e..6727b24 100644
--- a/res/color-v31/folder_preview_light.xml
+++ b/res/color-v31/folder_preview_light.xml
@@ -14,7 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item
-        android:color="@android:color/system_accent2_500"
-        android:lStar="80" />
+    <item android:color="@android:color/system_accent2_200"/>
 </selector>
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 5518dc8..31f4870 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -51,7 +51,7 @@
             android:singleLine="true"
             android:textColor="?attr/folderTextColor"
             android:textColorHighlight="?android:attr/colorControlHighlight"
-            android:textColorHint="?attr/folderHintColor"/>
+            android:textColorHint="?attr/folderHintTextColor"/>
 
         <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 0c036ff..9f09b99 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -34,8 +34,10 @@
     <color name="workspace_text_color_light">@android:color/system_neutral1_0</color>
     <color name="workspace_text_color_dark">@android:color/system_neutral1_1000</color>
 
-    <color name="folder_hint_text_color_light">@android:color/system_neutral1_50</color>
-    <color name="folder_hint_text_color_dark">@android:color/system_neutral2_700</color>
+    <color name="folder_text_color_light">@android:color/system_neutral1_900</color>
+    <color name="folder_text_color_dark">@android:color/system_neutral1_100</color>
+    <color name="folder_hint_text_color_light">@android:color/system_neutral2_700</color>
+    <color name="folder_hint_text_color_dark">@android:color/system_neutral2_200</color>
 
     <color name="text_color_primary_dark">@android:color/system_neutral1_50</color>
     <color name="text_color_secondary_dark">@android:color/system_neutral2_200</color>
@@ -44,7 +46,7 @@
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
 
     <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
-    <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
+    <color name="folder_pagination_color_dark">@android:color/system_accent1_200</color>
 
     <color name="home_settings_header_accent">@android:color/system_accent1_600</color>
     <color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 418f5a7..0772557 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -48,7 +48,7 @@
     <attr name="folderIconRadius" format="float" />
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
-    <attr name="folderHintColor" format="color" />
+    <attr name="folderHintTextColor" format="color" />
     <attr name="isFolderDarkText" format="boolean" />
     <attr name="workspaceAccentColor" format="color" />
     <attr name="dropTargetHoverTextColor" format="color" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index b1bff18..2295043 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -60,17 +60,19 @@
     <color name="workspace_text_color_light">#FFF</color>
     <color name="workspace_text_color_dark">#FF000000</color>
 
-    <color name="folder_hint_text_color_light">#FFF</color>
-    <color name="folder_hint_text_color_dark">#FF000000</color>
+    <color name="folder_text_color_light">#1F1F1F</color>
+    <color name="folder_text_color_dark">#E3E3E3</color>
+    <color name="folder_hint_text_color_light">#444746</color>
+    <color name="folder_hint_text_color_dark">#C4C7C5</color>
 
-    <color name="folder_background_light">#F9F9F9</color>
-    <color name="folder_background_dark">#464746</color>
+    <color name="folder_background_light">#EFEDED</color>
+    <color name="folder_background_dark">#1F2020</color>
 
-    <color name="folder_preview_light">#F9F9F9</color>
-    <color name="folder_preview_dark">#464746</color>
+    <color name="folder_preview_light">#7FCFFF</color>
+    <color name="folder_preview_dark">#2A2A2A</color>
 
-    <color name="folder_pagination_color_light">#ff006c5f</color>
-    <color name="folder_pagination_color_dark">#ffbfebe3</color>
+    <color name="folder_pagination_color_light">#0B57D0</color>
+    <color name="folder_pagination_color_dark">#A8C7FA</color>
 
     <color name="text_color_primary_dark">#FFFFFFFF</color>
     <color name="text_color_secondary_dark">#FFFFFFFF</color>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8f75550..d0f2067 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -54,9 +54,9 @@
         <item name="folderPreviewColor">@color/folder_preview_light</item>
         <item name="folderBackgroundColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
-        <item name="folderTextColor">?android:attr/textColorPrimary</item>
         <item name="isFolderDarkText">true</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_dark</item>
+        <item name="folderTextColor">@color/folder_text_color_light</item>
+        <item name="folderHintTextColor">@color/folder_hint_text_color_light</item>
         <item name="loadingIconColor">#CCFFFFFF</item>
         <item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
         <item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
@@ -116,7 +116,8 @@
         <item name="folderBackgroundColor">@color/folder_background_dark</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="isFolderDarkText">false</item>
-        <item name="folderHintColor">@color/folder_hint_text_color_light</item>
+        <item name="folderTextColor">@color/folder_text_color_dark</item>
+        <item name="folderHintTextColor">@color/folder_hint_text_color_dark</item>
         <item name="isMainColorDark">true</item>
         <item name="loadingIconColor">#99FFFFFF</item>
         <item name="iconOnlyShortcutColor">#B3FFFFFF</item>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 5163ede..6763eaf 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,6 +18,7 @@
 
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
@@ -571,7 +572,9 @@
     }
 
     protected void updateBgAlpha() {
-        mBackground.setAlpha((int) (mSpringLoadedProgress * 255));
+        if (!getWorkspace().mLauncher.isInState(EDIT_MODE)) {
+            mBackground.setAlpha((int) (mSpringLoadedProgress * 255));
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index faf5c6a..0b75c45 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -34,6 +34,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -44,6 +45,7 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
+import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
 import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
@@ -901,12 +903,8 @@
 
         final int pendingAddWidgetId = requestArgs.getWidgetId();
 
-        Runnable exitSpringLoaded = new Runnable() {
-            @Override
-            public void run() {
-                mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
-            }
-        };
+        Runnable exitSpringLoaded = MULTI_SELECT_EDIT_MODE.get() ? null
+                : () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
 
         if (requestCode == REQUEST_BIND_APPWIDGET) {
             // This is called only if the user did not previously have permissions to bind widgets
@@ -1040,10 +1038,9 @@
             final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId,
                     requestArgs.getWidgetHandler().getProviderInfo(this));
             boundWidget = layout;
-            onCompleteRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    completeAddAppWidget(appWidgetId, requestArgs, layout, null);
+            onCompleteRunnable = () -> {
+                completeAddAppWidget(appWidgetId, requestArgs, layout, null);
+                if (!isInState(EDIT_MODE)) {
                     mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
                 }
             };
@@ -1178,7 +1175,7 @@
         }
         addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
 
-        if (state == SPRING_LOADED) {
+        if (state == SPRING_LOADED || state == EDIT_MODE) {
             // Prevent any Un/InstallShortcutReceivers from updating the db while we are
             // not on homescreen
             ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP);
@@ -1532,7 +1529,8 @@
                 mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
                     @Override
                     public void onStateTransitionComplete(LauncherState finalState) {
-                        if (mPrevLauncherState == SPRING_LOADED && finalState == NORMAL) {
+                        if ((mPrevLauncherState == SPRING_LOADED || mPrevLauncherState == EDIT_MODE)
+                                && finalState == NORMAL) {
                             AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
                             mStateManager.removeStateListener(this);
                         }
@@ -1900,13 +1898,9 @@
                 REQUEST_CREATE_APPWIDGET)) {
             // If the configuration flow was not started, add the widget
 
-            Runnable onComplete = new Runnable() {
-                @Override
-                public void run() {
-                    // Exit spring loaded mode if necessary after adding the widget
-                    mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
-                }
-            };
+            // Exit spring loaded mode if necessary after adding the widget
+            Runnable onComplete = MULTI_SELECT_EDIT_MODE.get() ? null
+                    : () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
             completeAddAppWidget(appWidgetId, info, boundWidget,
                     addFlowHandler.getProviderInfo(this));
             mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index b8d13ed..8b124dc 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.EDIT_MODE_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -38,6 +39,7 @@
 
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.EditModeState;
 import com.android.launcher3.states.HintState;
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -103,7 +105,7 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[10];
+    private static final LauncherState[] sAllStates = new LauncherState[11];
 
     /**
      * TODO: Create a separate class for NORMAL state.
@@ -123,6 +125,7 @@
      */
     public static final LauncherState SPRING_LOADED = new SpringLoadedState(
             SPRING_LOADED_STATE_ORDINAL);
+    public static final LauncherState EDIT_MODE = new EditModeState(EDIT_MODE_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(
@@ -328,7 +331,9 @@
      * Gets the translation provider for workspace pages.
      */
     public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) {
-        if (this != SPRING_LOADED || !launcher.getDeviceProfile().isTwoPanels) {
+        if (this != SPRING_LOADED
+                || this != EDIT_MODE
+                || !launcher.getDeviceProfile().isTwoPanels) {
             return DEFAULT_PAGE_TRANSLATION_PROVIDER;
         }
         final float quarterPageSpacing = launcher.getWorkspace().getPageSpacing() / 4f;
@@ -343,6 +348,16 @@
         };
     }
 
+    /**
+     * Called when leaving this LauncherState
+     * @param launcher - Launcher instance
+     * @param toState - New LauncherState that is being entered
+     */
+    public void onLeavingState(Launcher launcher, LauncherState toState) {
+        // no-op
+        // override to handle when leaving current LauncherState
+    }
+
     @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
         // No history is supported
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2fa1a57..73bb828 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
 import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
 import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE;
@@ -494,8 +495,9 @@
             }
         }
 
-        // Always enter the spring loaded mode
-        mLauncher.getStateManager().goToState(SPRING_LOADED);
+        if (!mLauncher.isInState(EDIT_MODE)) {
+            mLauncher.getStateManager().goToState(SPRING_LOADED);
+        }
         mStatsLogManager.logger().withItemInfo(dragObject.dragInfo)
                 .withInstanceId(dragObject.logInstanceId)
                 .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
@@ -1432,7 +1434,8 @@
     }
 
     private boolean workspaceInScrollableState() {
-        return mLauncher.isInState(SPRING_LOADED) || !workspaceInModalState();
+        return mLauncher.isInState(SPRING_LOADED) || mLauncher.isInState(EDIT_MODE)
+                || !workspaceInModalState();
     }
 
     /**
@@ -1526,6 +1529,7 @@
     @Override
     public void setState(LauncherState toState) {
         onStartStateTransition();
+        mLauncher.getStateManager().getState().onLeavingState(mLauncher, toState);
         mStateTransitionAnimation.setState(toState);
         onEndStateTransition();
     }
@@ -1537,6 +1541,7 @@
     public void setStateWithAnimation(
             LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
         StateTransitionListener listener = new StateTransitionListener();
+        mLauncher.getStateManager().getState().onLeavingState(mLauncher, toState);
         mStateTransitionAnimation.setStateWithAnimation(toState, config, animation);
 
         // Invalidate the pages now, so that we have the visible pages before the
@@ -1999,7 +2004,9 @@
                         distance, false, d)
                         || addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
                         distance, d, false)) {
-                    mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+                    if (!mLauncher.isInState(EDIT_MODE)) {
+                        mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+                    }
                     return;
                 }
 
@@ -2128,14 +2135,19 @@
                     // spring-loaded mode so the page meets the icon where it was picked up.
                     final RunnableList callbackList = new RunnableList();
                     final Runnable onCompleteCallback = onCompleteRunnable;
+                    LauncherState currentState = mLauncher.getStateManager().getState();
                     mLauncher.getDragController().animateDragViewToOriginalPosition(
                             /* onComplete= */ callbackList::executeAllAndDestroy, cell,
-                            SPRING_LOADED.getTransitionDuration(mLauncher, true /* isToState */));
-                    mLauncher.getStateManager().goToState(NORMAL, /* delay= */ 0,
-                            onCompleteCallback == null
-                                    ? null
-                                    : forSuccessCallback(
-                                            () -> callbackList.add(onCompleteCallback)));
+                            currentState.getTransitionDuration(mLauncher, true /* isToState */));
+                    if (!mLauncher.isInState(EDIT_MODE)) {
+                        mLauncher.getStateManager().goToState(NORMAL, /* delay= */ 0,
+                                onCompleteCallback == null
+                                        ? null
+                                        : forSuccessCallback(
+                                                () -> callbackList.add(onCompleteCallback)));
+                    } else if (onCompleteCallback != null) {
+                        forSuccessCallback(() -> callbackList.add(onCompleteCallback));
+                    }
                     mLauncher.getDropTargetBar().onDragEnd();
                     parent.onDropChild(cell);
                     return;
@@ -2159,8 +2171,12 @@
             }
             parent.onDropChild(cell);
 
-            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
-                    onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));
+            if (!mLauncher.isInState(EDIT_MODE)) {
+                mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
+                        onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));
+            } else if (onCompleteRunnable != null) {
+                forSuccessCallback(onCompleteRunnable);
+            }
             mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                     .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
@@ -2734,7 +2750,8 @@
         final int screenId = getIdForScreen(cellLayout);
         if (!mLauncher.isHotseatLayout(cellLayout)
                 && screenId != getScreenIdForPageIndex(mCurrentPage)
-                && !mLauncher.isInState(SPRING_LOADED)) {
+                && !mLauncher.isInState(SPRING_LOADED)
+                && !mLauncher.isInState(EDIT_MODE)) {
             snapToPage(getPageIndexForScreenId(screenId));
         }
 
@@ -2812,7 +2829,6 @@
         } else {
             // This is for other drag/drop cases, like dragging from All Apps
             mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
-
             View view;
 
             switch (info.itemType) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 55ab7f1..565d7da 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -59,6 +59,7 @@
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.graphics.Scrim;
 import com.android.launcher3.graphics.SysUiScrim;
+import com.android.launcher3.states.EditModeState;
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.DynamicResource;
@@ -212,8 +213,8 @@
             PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
             StateAnimationConfig config) {
         float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
-        float springLoadedProgress = (state instanceof SpringLoadedState) ? 1.0f : 0f;
-
+        float springLoadedProgress =
+                (state instanceof  SpringLoadedState || state instanceof EditModeState) ? 1f : 0f;
         propertySetter.setFloat(cl,
                 CellLayout.SPRING_LOADED_PROGRESS, springLoadedProgress, ZOOM_OUT);
         Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index a671c6e..30af502 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -45,7 +45,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -53,7 +52,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.function.Predicate;
 
@@ -97,15 +95,7 @@
             StatsLogManager statsLogManager) {
         mUserManager = userManager;
         mAllApps = allApps;
-        boolean cloningChanges = FeatureFlags.ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER.get();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(WORK_TAB_MISSING, "matcher flag: " + cloningChanges);
-        }
-        if (cloningChanges) {
-            mMatcher = ofWorkProfileUser(userManager);
-        } else {
-            mMatcher = mAllApps.mPersonalMatcher.negate();
-        }
+        mMatcher = mAllApps.mPersonalMatcher.negate();
         mStatsLogManager = statsLogManager;
     }
 
@@ -280,27 +270,4 @@
             }
         };
     }
-
-    /**
-     * Filter to only display apps in managed profile in work tab.
-     */
-    private Predicate<ItemInfo> ofWorkProfileUser(UserManager um) {
-        return info -> info != null && isManagedProfile(um, info.user.hashCode());
-    }
-
-
-    private static boolean isManagedProfile(UserManager um, int userId) {
-        try {
-            // isManagedProfile is a @SystemApi.
-            String methodName = "isManagedProfile";
-            Method method = um.getClass().getDeclaredMethod(methodName, int.class);
-            Object result = method.invoke(um, userId);
-            if (result instanceof Boolean) {
-                return (boolean) result;
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to call #isManagedProfile via reflection from Launcher");
-        }
-        return false;
-    }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 6699825..621c2ab 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -213,11 +213,6 @@
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
             "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
 
-    // TODO(Block 15): Clean up flags
-    public static final BooleanFlag ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER = getDebugFlag(266177840,
-            "ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER", DISABLED,
-            "Removes clone apps from the work profile tab.");
-
     // TODO(Block 16): Clean up flags
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012,
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index aaa5b1a..da6f446 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -157,7 +158,9 @@
 
     @Override
     protected void exitDrag() {
-        mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        if (!mActivity.isInState(EDIT_MODE)) {
+            mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragView.java b/src/com/android/launcher3/dragndrop/LauncherDragView.java
index cc68e2e..4c43865 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragView.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragView.java
@@ -57,7 +57,8 @@
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
         setVisibility((finalState == LauncherState.NORMAL
-                || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE);
+                || finalState == LauncherState.SPRING_LOADED
+                || finalState == LauncherState.EDIT_MODE) ? VISIBLE : INVISIBLE);
     }
 
     @Override
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index dd74125..7bdec1c 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -16,6 +16,11 @@
 
 package com.android.launcher3.dragndrop;
 
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
+
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.ComponentName;
@@ -29,10 +34,8 @@
 import android.os.Process;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -90,11 +93,11 @@
 
     @Override
     public WorkspaceItemInfo createWorkspaceItemInfo() {
+        long transitionDuration = (MULTI_SELECT_EDIT_MODE.get() ? EDIT_MODE : SPRING_LOADED)
+                .getTransitionDuration(Launcher.getLauncher(mContext), true /* isToState */);
         // Total duration for the drop animation to complete.
         long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) +
-                LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
-                LauncherState.SPRING_LOADED.getTransitionDuration(Launcher.getLauncher(mContext),
-                        true /* isToState */);
+                SPRING_LOADED_EXIT_DELAY + transitionDuration;
         // Delay the actual accept() call until the drop animation is complete.
         return PinRequestHelper.createWorkspaceItemFromPinItemRequest(
                 mContext, mRequestSupplier.get(), duration);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3c31b7a..4ae54e6 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -19,6 +19,7 @@
 import static android.text.TextUtils.isEmpty;
 
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
@@ -558,7 +559,7 @@
                 .inflate(R.layout.user_folder_icon_normalized, null);
     }
 
-    private void startAnimation(final AnimatorSet a) {
+    private void addAnimationStartListeners(AnimatorSet a) {
         mLauncherDelegate.forEachVisibleWorkspacePage(
                 visiblePage -> addAnimatorListenerForPage(a, (CellLayout) visiblePage));
 
@@ -574,7 +575,6 @@
                 mCurrentAnimator = null;
             }
         });
-        a.start();
     }
 
     private void addAnimatorListenerForPage(AnimatorSet a, CellLayout currentCellLayout) {
@@ -734,10 +734,14 @@
 
         mPageIndicator.stopAllAnimations();
 
+        // b/282158620 because setCurrentPlayTime() below will start animator, we need to register
+        // {@link AnimatorListener} before it so that {@link AnimatorListener#onAnimationStart} can
+        // be called to register mCurrentAnimator, which will be used to cancel animator
+        addAnimationStartListeners(anim);
         // Because t=0 has the folder match the folder icon, we can skip the
         // first frame and have the same movement one frame earlier.
         anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
-        startAnimation(anim);
+        anim.start();
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
@@ -815,7 +819,8 @@
                 mIsAnimatingClosed = false;
             }
         });
-        startAnimation(a);
+        addAnimationStartListeners(a);
+        a.start();
     }
 
     @Override
@@ -1339,7 +1344,10 @@
                     mLauncherDelegate.getModelWriter());
         }
 
-        launcher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        if (!launcher.isInState(EDIT_MODE)) {
+            launcher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        }
+
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 05ad57a..2ce6c78 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -265,23 +265,37 @@
         Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
         play(a, z, mIsOpening ? midDuration : 0, midDuration);
 
-        // Store clip variables
-        CellLayout cellLayout = mContent.getCurrentCellLayout();
-        boolean folderClipChildren = mFolder.getClipChildren();
-        boolean folderClipToPadding = mFolder.getClipToPadding();
-        boolean contentClipChildren = mContent.getClipChildren();
-        boolean contentClipToPadding = mContent.getClipToPadding();
-        boolean cellLayoutClipChildren = cellLayout.getClipChildren();
-        boolean cellLayoutClipPadding = cellLayout.getClipToPadding();
-
-        mFolder.setClipChildren(false);
-        mFolder.setClipToPadding(false);
-        mContent.setClipChildren(false);
-        mContent.setClipToPadding(false);
-        cellLayout.setClipChildren(false);
-        cellLayout.setClipToPadding(false);
-
+        // Store clip variables.
+        // Because {@link #onAnimationStart} and {@link #onAnimationEnd} callbacks are sent to
+        // message queue and executed on separate frame, we should save states in
+        // {@link #onAnimationStart} instead of before creating animator, so that cancelling
+        // animation A and restarting animation B allows A to reset states in
+        // {@link #onAnimationEnd} before B reads new UI state from {@link #onAnimationStart}.
         a.addListener(new AnimatorListenerAdapter() {
+            private CellLayout mCellLayout;
+            private boolean mFolderClipToPadding;
+            private boolean mContentClipChildren;
+            private boolean mContentClipToPadding;
+            private boolean mCellLayoutClipChildren;
+            private boolean mCellLayoutClipPadding;
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                super.onAnimationStart(animator);
+                mCellLayout = mContent.getCurrentCellLayout();
+                mFolderClipToPadding = mFolder.getClipToPadding();
+                mContentClipChildren = mContent.getClipChildren();
+                mContentClipToPadding = mContent.getClipToPadding();
+                mCellLayoutClipChildren = mCellLayout.getClipChildren();
+                mCellLayoutClipPadding = mCellLayout.getClipToPadding();
+
+                mFolder.setClipToPadding(false);
+                mContent.setClipChildren(false);
+                mContent.setClipToPadding(false);
+                mCellLayout.setClipChildren(false);
+                mCellLayout.setClipToPadding(false);
+            }
+
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
@@ -295,13 +309,11 @@
                 mFolder.mFooter.setTranslationX(0f);
                 mFolder.mFolderName.setAlpha(1f);
 
-                mFolder.setClipChildren(folderClipChildren);
-                mFolder.setClipToPadding(folderClipToPadding);
-                mContent.setClipChildren(contentClipChildren);
-                mContent.setClipToPadding(contentClipToPadding);
-                cellLayout.setClipChildren(cellLayoutClipChildren);
-                cellLayout.setClipToPadding(cellLayoutClipPadding);
-
+                mFolder.setClipToPadding(mFolderClipToPadding);
+                mContent.setClipChildren(mContentClipChildren);
+                mContent.setClipToPadding(mContentClipToPadding);
+                mCellLayout.setClipChildren(mCellLayoutClipChildren);
+                mCellLayout.setClipToPadding(mCellLayoutClipPadding);
             }
         });
 
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 8f0b8ec..e89c0c5 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -51,7 +51,9 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.GridSizeMigrationUtil;
+import com.android.launcher3.model.LauncherBinder;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.provider.LauncherDbUtils;
@@ -226,12 +228,14 @@
             LauncherAppState.getInstance(previewContext)
                     .getModel().getModelDbController().clearEmptyDbFlag();
 
+            BgDataModel bgModel = new BgDataModel();
             new LoaderTask(
                     LauncherAppState.getInstance(previewContext),
                     /* bgAllAppsList= */ null,
-                    new BgDataModel(),
+                    bgModel,
                     LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
-                    /* results= */ null) {
+                    new LauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
+                            /* bgAllAppsList= */ null, new Callbacks[0])) {
 
                 @Override
                 public void run() {
diff --git a/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
index a6c897f..408a5a0 100644
--- a/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
+++ b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
@@ -17,6 +17,7 @@
 
 import static android.app.Activity.DEFAULT_KEYS_SEARCH_LOCAL;
 
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 
 import android.app.Activity;
@@ -114,7 +115,7 @@
 
     @Override
     public void onStateTransitionStart(LauncherState toState) {
-        if (toState != SPRING_LOADED) {
+        if (toState != SPRING_LOADED && toState != EDIT_MODE) {
             close(false);
         }
     }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d4eded5..1a8cf24 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -51,6 +51,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
@@ -118,6 +119,7 @@
 
     private static final boolean DEBUG = true;
 
+    @NonNull
     protected final LauncherAppState mApp;
     private final AllAppsList mBgAllAppsList;
     protected final BgDataModel mBgDataModel;
@@ -125,6 +127,7 @@
 
     private FirstScreenBroadcast mFirstScreenBroadcast;
 
+    @NonNull
     private final LauncherBinder mLauncherBinder;
 
     private final LauncherApps mLauncherApps;
@@ -145,11 +148,11 @@
     private boolean mItemsDeleted = false;
     private String mDbName;
 
-    public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
-            ModelDelegate modelDelegate, LauncherBinder launcherBinder) {
+    public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
+            ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) {
         mApp = app;
         mBgAllAppsList = bgAllAppsList;
-        mBgDataModel = dataModel;
+        mBgDataModel = bgModel;
         mModelDelegate = modelDelegate;
         mLauncherBinder = launcherBinder;
 
diff --git a/src/com/android/launcher3/states/EditModeState.kt b/src/com/android/launcher3/states/EditModeState.kt
new file mode 100644
index 0000000..aafaaa0
--- /dev/null
+++ b/src/com/android/launcher3/states/EditModeState.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 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.states
+
+import android.content.Context
+import com.android.launcher3.Launcher
+import com.android.launcher3.LauncherState
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.views.ActivityContext
+
+/** Definition for Edit Mode state used for home gardening multi-select */
+class EditModeState(id: Int) : LauncherState(id, StatsLogManager.LAUNCHER_STATE_HOME, STATE_FLAGS) {
+
+    companion object {
+        private val STATE_FLAGS =
+            (FLAG_MULTI_PAGE or
+                FLAG_WORKSPACE_INACCESSIBLE or
+                FLAG_DISABLE_RESTORE or
+                FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED or
+                FLAG_WORKSPACE_HAS_BACKGROUNDS)
+    }
+
+    override fun <T> getTransitionDuration(context: T, isToState: Boolean): Int where
+    T : Context?,
+    T : ActivityContext? {
+        return 150
+    }
+
+    override fun <T> getDepthUnchecked(context: T): Float where T : Context?, T : ActivityContext? {
+        return 0.5f
+    }
+
+    override fun getWorkspaceScaleAndTranslation(launcher: Launcher): ScaleAndTranslation {
+        val scale = launcher.deviceProfile.getWorkspaceSpringLoadScale(launcher)
+        return ScaleAndTranslation(scale, 0f, 0f)
+    }
+
+    override fun getHotseatScaleAndTranslation(launcher: Launcher): ScaleAndTranslation {
+        val scale = launcher.deviceProfile.getWorkspaceSpringLoadScale(launcher)
+        return ScaleAndTranslation(scale, 0f, 0f)
+    }
+
+    override fun getWorkspaceBackgroundAlpha(launcher: Launcher): Float {
+        return 0.2f
+    }
+
+    override fun onLeavingState(launcher: Launcher?, toState: LauncherState?) {
+        // cleanup any changes to workspace
+    }
+}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 7db7b0d..9cba19d 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -19,6 +19,7 @@
 import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED;
@@ -58,7 +59,11 @@
         }
         Launcher launcher = Launcher.getLauncher(v.getContext());
         if (!canStartDrag(launcher)) return false;
-        if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
+        if (!launcher.isInState(NORMAL)
+                && !launcher.isInState(OVERVIEW)
+                && !launcher.isInState(EDIT_MODE)) {
+            return false;
+        }
         if (!(v.getTag() instanceof ItemInfo)) return false;
 
         launcher.setWaitingForResult(null);
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index badcd35..6a4e528 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -136,16 +136,19 @@
                 if (mDestroyed) {
                     Log.e(TAG, "Static object access with a destroyed context");
                 }
-                if (!mAllowedObjects.contains(object)) {
-                    throw new IllegalStateException(
-                            "Leaking unknown objects " + object + "  " + provider);
-                }
+
                 T t = (T) mObjectMap.get(object);
                 if (t != null) {
                     return t;
                 }
                 if (Looper.myLooper() == Looper.getMainLooper()) {
                     t = createObject(provider);
+                    // Check if we've explicitly allowed the object or if it's a SafeCloseable,
+                    // it will get destroyed in onDestroy()
+                    if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
+                        throw new IllegalStateException(
+                                "Leaking unknown objects " + object + "  " + provider + " " + t);
+                    }
                     mObjectMap.put(object, t);
                     mOrderedObjects.add(t);
                     return t;
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index ba6165f..4641e31 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -17,6 +17,7 @@
 
 import static androidx.core.content.ContextCompat.getColorStateList;
 
+import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
@@ -222,6 +223,8 @@
     }
 
     private static boolean enterHomeGardening(View view) {
+        Launcher launcher = Launcher.getLauncher(view.getContext());
+        launcher.getStateManager().goToState(EDIT_MODE);
         return true;
     }
 
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 601b07e..2b67cdd 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -36,6 +36,7 @@
     public static final int HINT_STATE_ORDINAL = 7;
     public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8;
     public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9;
+    public static final int EDIT_MODE_STATE_ORDINAL = 10;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
     public static final String SEQUENCE_MAIN = "Main";
     public static final String SEQUENCE_TIS = "TIS";
@@ -63,6 +64,8 @@
                 return "Hint2Button";
             case OVERVIEW_SPLIT_SELECT_ORDINAL:
                 return "OverviewSplitSelect";
+            case EDIT_MODE_STATE_ORDINAL:
+                return "EditMode";
             default:
                 return "Unknown";
         }