Merge "Refactoring instance variable for ArrowPopup.java" into udc-dev
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 4f9c32a..3fddd9d 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -218,29 +218,6 @@
                 }
             }
 
-            case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
-                useTestWorkspaceLayout(
-                        LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST);
-                return response;
-            }
-
-            case TestProtocol.REQUEST_USE_TEST2_WORKSPACE_LAYOUT: {
-                useTestWorkspaceLayout(
-                        LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2);
-                return response;
-            }
-
-            case TestProtocol.REQUEST_USE_TAPL_WORKSPACE_LAYOUT: {
-                useTestWorkspaceLayout(
-                        LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TAPL);
-                return response;
-            }
-
-            case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
-                useTestWorkspaceLayout(null);
-                return response;
-            }
-
             case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
                 return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
                     ShortcutAndWidgetContainer hotseatIconsContainer =
@@ -278,20 +255,4 @@
                 return super.call(method, arg, extras);
         }
     }
-
-    private void useTestWorkspaceLayout(String layout) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            if (layout != null) {
-                LauncherSettings.Settings.call(mContext.getContentResolver(),
-                        LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG,
-                        layout);
-            } else {
-                LauncherSettings.Settings.call(mContext.getContentResolver(),
-                        LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index b74964c..fdef39f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -93,15 +93,14 @@
     protected void init(TaskbarControllers taskbarControllers) {
         super.init(taskbarControllers);
 
-        mTaskbarLauncherStateController.init(mControllers, mLauncher);
+        mTaskbarLauncherStateController.init(mControllers, mLauncher,
+                mControllers.getSharedState().sysuiStateFlags);
 
         mLauncher.setTaskbarUIController(this);
 
         onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
 
         onStashedInAppChanged(mLauncher.getDeviceProfile());
-        mTaskbarLauncherStateController.updateStateForSysuiFlags(
-                mControllers.getSharedState().sysuiStateFlags, true /* fromInit */);
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
 
         // Restore the in-app display progress from before Taskbar was recreated.
@@ -325,8 +324,8 @@
     }
 
     @Override
-    public void updateStateForSysuiFlags(int sysuiFlags, boolean skipAnim) {
-        mTaskbarLauncherStateController.updateStateForSysuiFlags(sysuiFlags, skipAnim);
+    public void updateStateForSysuiFlags(int sysuiFlags) {
+        mTaskbarLauncherStateController.updateStateForSysuiFlags(sysuiFlags);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index a713ff5..9a9e0ba 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -714,6 +714,8 @@
      * setup wizard, or normal 3 button nav.
      */
     private void updateButtonLayoutSpacing() {
+        boolean isThreeButtonNav = mContext.isThreeButtonNav();
+
         DeviceProfile dp = mContext.getDeviceProfile();
         Resources res = mContext.getResources();
         boolean isInSetup = !mContext.isUserSetupComplete();
@@ -721,7 +723,9 @@
         boolean isInKidsMode = mContext.isNavBarKidsModeActive();
 
         if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
-            boolean isThreeButtonNav = mContext.isThreeButtonNav();
+            if (!isThreeButtonNav) {
+                return;
+            }
 
             NavButtonLayoutter navButtonLayoutter =
                     NavButtonLayoutFactory.Companion.getUiLayoutter(
@@ -803,7 +807,7 @@
             mNavButtonContainer.requestLayout();
 
             mHomeButton.setOnLongClickListener(null);
-        } else if (mContext.isThreeButtonNav()) {
+        } else if (isThreeButtonNav) {
             final RotateDrawable rotateDrawable = new RotateDrawable();
             rotateDrawable.setDrawable(mContext.getDrawable(R.drawable.ic_sysbar_back));
             rotateDrawable.setFromDegrees(0f);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5c53b5f..fe1c9d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -186,7 +186,8 @@
         mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
 
         // Inflate views.
-        int taskbarLayout = DisplayController.isTransientTaskbar(this)
+        boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
+        int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode
                 ? R.layout.transient_taskbar
                 : R.layout.taskbar;
         mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
@@ -254,6 +255,12 @@
                 sharedState.systemBarAttrsBehavior);
         onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity);
 
+        if (FLAG_HIDE_NAVBAR_WINDOW) {
+            // W/ the flag not set this entire class gets re-created, which resets the value of
+            // mIsDestroyed. We re-use the class for small-screen, so we explicitly have to mark
+            // this class as non-destroyed
+            mIsDestroyed = false;
+        }
 
         if (!mAddedWindow) {
             mWindowManager.addView(mDragLayer, mWindowLayoutParams);
@@ -334,7 +341,7 @@
     public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
         DeviceProfile deviceProfile = getDeviceProfile();
         // Taskbar is on the logical bottom of the screen
-        boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) &&
+        boolean isVerticalBarLayout = TaskbarManager.isPhoneButtonNavMode(this) &&
                 deviceProfile.isLandscape;
 
         int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -592,7 +599,7 @@
         mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
                 (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
 
-        mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
+        mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 07cea01..fe365f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -16,11 +16,13 @@
 
 package com.android.launcher3.taskbar
 
+import android.content.res.Resources
 import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.RectF
+import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
 import com.android.launcher3.Utilities.mapRange
@@ -61,7 +63,7 @@
     private val invertedLeftCornerPath: Path = Path()
     private val invertedRightCornerPath: Path = Path()
 
-    private val stashedHandleWidth =
+    private var stashedHandleWidth =
         context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width)
 
     private val stashedHandleHeight =
@@ -86,6 +88,13 @@
         setCornerRoundness(DEFAULT_ROUNDNESS)
     }
 
+    fun updateStashedHandleWidth(dp: DeviceProfile, res: Resources) {
+        stashedHandleWidth = res.getDimensionPixelSize(
+                if (TaskbarManager.isPhoneMode(dp)) R.dimen.taskbar_stashed_small_screen
+                else R.dimen.taskbar_stashed_handle_width
+        )
+    }
+
     /**
      * Sets the roundness of the round corner above Taskbar. No effect on transient Taskkbar.
      *
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index c53460d..7681fe0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -71,7 +71,7 @@
 
     public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
         mControllerCallbacks = callbacks;
-
+        mBackgroundRenderer.updateStashedHandleWidth(mActivity.getDeviceProfile(), getResources());
         recreateControllers();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index fcb2042..c029097 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -21,6 +21,7 @@
 import android.os.IBinder
 import android.view.InsetsFrameProvider
 import android.view.InsetsFrameProvider.SOURCE_DISPLAY
+import android.view.InsetsSource.FLAG_SUPPRESS_SCRIM
 import android.view.ViewTreeObserver
 import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME
 import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
@@ -86,7 +87,8 @@
         if (context.isGestureNav) {
             windowLayoutParams.providedInsets =
                     arrayOf(
-                            InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+                            InsetsFrameProvider(insetsOwner, 0, navigationBars())
+                                    .setFlags(FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM),
                             InsetsFrameProvider(insetsOwner, 0, tappableElement()),
                             InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
                             InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 9bc8cdd..59b5e74 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -210,7 +210,9 @@
                 }
             };
 
-    public void init(TaskbarControllers controllers, QuickstepLauncher launcher) {
+    /** Initializes the controller instance, and applies the initial state immediately. */
+    public void init(TaskbarControllers controllers, QuickstepLauncher launcher,
+            int sysuiStateFlags) {
         mCanSyncViews = false;
 
         mControllers = controllers;
@@ -229,6 +231,8 @@
 
         mLauncher.getStateManager().addStateListener(mStateListener);
         mLauncherState = launcher.getStateManager().getState();
+        updateStateForSysuiFlags(sysuiStateFlags, /*applyState*/ false);
+
         applyState(0);
 
         mCanSyncViews = true;
@@ -298,7 +302,11 @@
     }
 
     /** SysUI flags updated, see QuickStepContract.SYSUI_STATE_* values. */
-    public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) {
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
+    }
+
+    private  void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
         final boolean prevIsAwake = hasAnyFlag(FLAG_AWAKE);
         final boolean currIsAwake = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_AWAKE);
 
@@ -322,9 +330,7 @@
                 || (systemUiStateFlags & SYSUI_STATE_WAKEFULNESS_MASK) != WAKEFULNESS_AWAKE;
         updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden);
 
-        if (skipAnim) {
-            applyState(0);
-        } else {
+        if (applyState) {
             applyState();
         }
     }
@@ -361,10 +367,6 @@
         applyState(duration, true);
     }
 
-    public Animator applyState(boolean start) {
-        return applyState(mControllers.taskbarStashController.getStashDuration(), start);
-    }
-
     public Animator applyState(long duration, boolean start) {
         if (mControllers.taskbarActivityContext.isDestroyed()) {
             return null;
@@ -420,7 +422,7 @@
             }
         }
 
-        if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER_ACTIVE | FLAG_AWAKE)) {
+        if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER_ACTIVE)) {
             animatorSet.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 90fcd37..5abeac7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -137,6 +137,7 @@
         if (folder != null) {
             folder.iterateOverItems(op);
         }
+        mControllers.taskbarAllAppsController.updateNotificationDots(updatedDots);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 1435cb0..f3e2ee2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -127,7 +127,7 @@
     /**
      * SysUI flags updated, see QuickStepContract.SYSUI_STATE_* values.
      */
-    public void updateStateForSysuiFlags(int sysuiFlags, boolean skipAnim){
+    public void updateStateForSysuiFlags(int sysuiFlags) {
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index a3e6814..6034739 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -114,7 +114,8 @@
         mActivityContext = ActivityContext.lookupContext(context);
         mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
         Resources resources = getResources();
-        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext);
+        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
+                && !TaskbarManager.isPhoneMode(mActivityContext.getDeviceProfile());
         mIsRtl = Utilities.isRtl(resources);
         mTransientTaskbarMinWidth = mContext.getResources().getDimension(
                 R.dimen.transient_taskbar_min_width);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 4a95a8f..0c9dc5b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -24,8 +24,10 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.taskbar.TaskbarControllers;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.util.PackageUserKey;
 
 import java.util.List;
+import java.util.function.Predicate;
 
 /**
  * Handles the all apps overlay window initialization, updates, and its data.
@@ -91,6 +93,13 @@
         }
     }
 
+    /** Updates the current notification dots. */
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
+        if (mAppsView != null) {
+            mAppsView.getAppsStore().updateNotificationDots(updatedDots);
+        }
+    }
+
     /** Opens the {@link TaskbarAllAppsContainerView} in a new window. */
     public void show() {
         show(true);
@@ -135,7 +144,6 @@
         overlayContext.getDragController().setDisallowLongClick(mDisallowLongClick);
     }
 
-
     @VisibleForTesting
     public int getTaskbarAllAppsTopPadding() {
         // Allow null-pointer since this should only be null if the apps view is not showing.
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 9a23557..d3e4ce5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -98,7 +98,7 @@
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
-    private @SystemUiStateFlags int mSystemUiStateFlags;
+    private @SystemUiStateFlags int mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
     private NavigationMode mMode = THREE_BUTTONS;
     private NavBarPosition mNavBarPosition;
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1cf682b..fdf0c6a 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -77,11 +77,11 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -678,7 +678,9 @@
 
         if (mUncheckedConsumer != InputConsumer.NO_OP) {
             switch (event.getActionMasked()) {
-                case ACTION_DOWN, ACTION_UP ->
+                case ACTION_DOWN:
+                    // fall through
+                case ACTION_UP:
                     ActiveGestureLog.INSTANCE.addLog(
                             /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
                                     + (int) event.getRawY() + "): "
@@ -687,15 +689,18 @@
                             /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
                                     ? MOTION_DOWN
                                     : MOTION_UP);
-                case ACTION_MOVE ->
+                    break;
+                case ACTION_MOVE:
                     ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
                             + MotionEvent.actionToString(event.getActionMasked()) + ","
                             + MotionEvent.classificationToString(event.getClassification())
                             + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE);
-                default ->
+                    break;
+                default: {
                     ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
                             + MotionEvent.actionToString(event.getActionMasked()) + ","
                             + MotionEvent.classificationToString(event.getClassification()));
+                }
             }
         }
 
@@ -835,12 +840,18 @@
 
             // If Taskbar is present, we listen for long press to unstash it.
             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
-            if (tac != null && canStartSystemGesture) {
-                reasonString.append(NEWLINE_PREFIX)
-                        .append(reasonPrefix)
-                        .append(SUBSTRING_PREFIX)
-                        .append("TaskbarActivityContext != null, using TaskbarStashInputConsumer");
-                base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
+            if (tac != null) {
+                // Present always on large screen or on small screen w/ flag
+                DeviceProfile dp = tac.getDeviceProfile();
+                boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp);
+                if (canStartSystemGesture && useTaskbarConsumer) {
+                    reasonString.append(NEWLINE_PREFIX)
+                            .append(reasonPrefix)
+                            .append(SUBSTRING_PREFIX)
+                            .append("TaskbarActivityContext != null, "
+                                    + "using TaskbarStashInputConsumer");
+                    base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
+                }
             }
 
             if (mDeviceState.isBubblesExpanded()) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 735c5e6..6243471 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -29,6 +29,8 @@
 
 import com.android.launcher3.tapl.Taskbar;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
@@ -49,11 +51,17 @@
     private static final String CALCULATOR_APP_PACKAGE =
             resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
 
+    private AutoCloseable mLauncherLayout;
+
     @Override
     public void setUp() throws Exception {
         Assume.assumeTrue(mLauncher.isTablet());
         super.setUp();
-        mLauncher.useTestWorkspaceLayoutOnReload();
+
+        LauncherLayoutBuilder layoutBuilder = new LauncherLayoutBuilder().atHotseat(0).putApp(
+                "com.google.android.apps.nexuslauncher.tests",
+                "com.android.launcher3.testcomponent.BaseTestingActivity");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
         TaplTestsLauncher3.initialize(this);
 
         startAppFast(CALCULATOR_APP_PACKAGE);
@@ -62,9 +70,11 @@
     }
 
     @After
-    public void tearDown() {
-        mLauncher.useDefaultWorkspaceLayoutOnReload();
+    public void tearDown() throws Exception {
         mLauncher.enableBlockTimeout(false);
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
     }
 
     @Test
diff --git a/res/drawable/drop_target_frame.xml b/res/drawable/drop_target_frame.xml
index 9f04103..0a73b3f 100644
--- a/res/drawable/drop_target_frame.xml
+++ b/res/drawable/drop_target_frame.xml
@@ -17,6 +17,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
     <solid android:color="@android:color/transparent" />
-    <corners android:radius="80dp" />
+    <corners android:radius="@dimen/drop_target_button_frame_radius" />
     <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/drop_target_frame_hover.xml b/res/drawable/drop_target_frame_hover.xml
index ddf3a4d..6acffd154 100644
--- a/res/drawable/drop_target_frame_hover.xml
+++ b/res/drawable/drop_target_frame_hover.xml
@@ -17,5 +17,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
     <solid android:color="?attr/workspaceAccentColor" />
-    <corners android:radius="28dp" />
+    <corners android:radius="@dimen/drop_target_button_frame_radius" />
 </shape>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 923883b..1aa1871 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -233,6 +233,7 @@
     <dimen name="drop_target_button_drawable_padding">8dp</dimen>
     <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
+    <dimen name="drop_target_button_frame_radius">80dp</dimen>
     <dimen name="drop_target_button_gap">28dp</dimen>
     <dimen name="drop_target_button_workspace_edge_gap">0dp</dimen>
 
diff --git a/res/xml/default_tapl_test_workspace.xml b/res/xml/default_tapl_test_workspace.xml
deleted file mode 100644
index 24d76f3..0000000
--- a/res/xml/default_tapl_test_workspace.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <favorite
-        launcher:container="-101"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="0"
-        launcher:className="com.google.android.apps.chrome.Main"
-        launcher:packageName="com.android.chrome" />
-
-</favorites>
diff --git a/res/xml/default_test2_workspace.xml b/res/xml/default_test2_workspace.xml
deleted file mode 100644
index c560104..0000000
--- a/res/xml/default_test2_workspace.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Dialer Messaging Chrome Camera -->
-    <favorite
-        launcher:container="-101"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="0"
-        launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
-        launcher:packageName="com.google.android.dialer" />
-
-    <favorite
-        launcher:container="-101"
-        launcher:screen="1"
-        launcher:x="1"
-        launcher:y="0"
-        launcher:className="com.google.android.apps.messaging.ui.ConversationListActivity"
-        launcher:packageName="com.google.android.apps.messaging" />
-
-    <favorite
-        launcher:container="-101"
-        launcher:screen="2"
-        launcher:x="2"
-        launcher:y="0"
-        launcher:className="com.google.android.apps.chrome.Main"
-        launcher:packageName="com.android.chrome" />
-
-    <favorite
-        launcher:container="-101"
-        launcher:screen="3"
-        launcher:x="3"
-        launcher:y="0"
-        launcher:className="com.android.camera.CameraLauncher"
-        launcher:packageName="com.google.android.GoogleCamera" />
-
-    <!-- Bottom row -->
-    <!-- Maps [space] [space] Play -->
-    <favorite
-        launcher:className="com.google.android.maps.MapsActivity"
-        launcher:packageName="com.google.android.apps.maps"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="-1" />
-
-    <favorite
-        launcher:className="com.android.vending.AssetBrowserActivity"
-        launcher:packageName="com.android.vending"
-        launcher:screen="0"
-        launcher:x="3"
-        launcher:y="-1" />
-
-    <!-- TODO: Place weather widget when it's available -->
-
-</favorites>
diff --git a/res/xml/default_test_workspace.xml b/res/xml/default_test_workspace.xml
deleted file mode 100644
index bd718b3..0000000
--- a/res/xml/default_test_workspace.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.
--->
-
-<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <!-- Launcher Test Activity -->
-    <resolve
-        launcher:container="-101"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.CATEGORY_TEST;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;end" />
-    </resolve>
-
-</favorites>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index e543370..f041ffb 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -16,6 +16,7 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -51,8 +52,8 @@
      * Updates the app widgets whose id has changed during the restore process.
      */
     @WorkerThread
-    public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds,
-            @NonNull AppWidgetHost host) {
+    public static void restoreAppWidgetIds(Context context, DatabaseHelper helper,
+            int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             Log.e(TAG, "Skipping widget ID remap as widgets not supported");
             host.deleteHost();
@@ -90,14 +91,16 @@
             String oldWidgetId = Integer.toString(oldWidgetIds[i]);
             final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
             final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
-            int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args))
+            int result = new ContentWriter(context,
+                            new ContentWriter.CommitParams(helper, where, args))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
             if (result == 0) {
-                Cursor cursor = cr.query(Favorites.CONTENT_URI,
+                Cursor cursor = helper.getWritableDatabase().query(
+                        Favorites.TABLE_NAME,
                         new String[] {Favorites.APPWIDGET_ID},
-                        "appWidgetId=?", new String[] { oldWidgetId }, null);
+                        "appWidgetId=?", new String[] { oldWidgetId }, null, null, null);
                 try {
                     if (!cursor.moveToFirst()) {
                         // The widget no long exists.
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 197aa5a..ede7e2f 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -27,6 +27,8 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.XmlResourceParser;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.Bundle;
@@ -38,7 +40,9 @@
 import android.util.Xml;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 import androidx.annotation.WorkerThread;
+import androidx.annotation.XmlRes;
 
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -161,7 +165,7 @@
     protected final LayoutParserCallback mCallback;
 
     protected final PackageManager mPackageManager;
-    protected final Resources mSourceRes;
+    protected final SourceResources mSourceRes;
     protected final Supplier<XmlPullParser> mInitialLayoutSupplier;
 
     private final InvariantDeviceProfile mIdp;
@@ -178,11 +182,12 @@
     public AutoInstallsLayout(Context context, LauncherWidgetHolder appWidgetHolder,
             LayoutParserCallback callback, Resources res,
             int layoutId, String rootTag) {
-        this(context, appWidgetHolder, callback, res, () -> res.getXml(layoutId), rootTag);
+        this(context, appWidgetHolder, callback, SourceResources.wrap(res),
+                () -> res.getXml(layoutId), rootTag);
     }
 
     public AutoInstallsLayout(Context context, LauncherWidgetHolder appWidgetHolder,
-            LayoutParserCallback callback, Resources res,
+            LayoutParserCallback callback, SourceResources res,
             Supplier<XmlPullParser> initialLayoutSupplier, String rootTag) {
         mContext = context;
         mAppWidgetHolder = appWidgetHolder;
@@ -703,4 +708,42 @@
         to.put(key, from.getAsInteger(key));
     }
 
+    /**
+     * Wrapper over resources for easier abstraction
+     */
+    public interface SourceResources {
+
+        /**
+         * Refer {@link Resources#getXml(int)}
+         */
+        default XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
+            throw new NotFoundException();
+        }
+
+        /**
+         * Refer {@link Resources#getString(int)}
+         */
+        default String getString(@StringRes int id) throws NotFoundException {
+            throw new NotFoundException();
+        }
+
+        /**
+         * Returns a {@link SourceResources} corresponding to the provided resources
+         */
+        static SourceResources wrap(Resources res) {
+            return new SourceResources() {
+                @Override
+                public XmlResourceParser getXml(int id) {
+                    return res.getXml(id);
+                }
+
+                @Override
+                public String getString(int id) {
+                    return res.getString(id);
+                }
+            };
+        }
+    }
+
+
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 06ac44a..0e027f8 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.LauncherBinder;
 import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
@@ -94,6 +95,8 @@
     @NonNull
     private final LauncherAppState mApp;
     @NonNull
+    private final ModelDbController mModelDbController;
+    @NonNull
     private final Object mLock = new Object();
     @Nullable
     private LoaderTask mLoaderTask;
@@ -143,6 +146,7 @@
             @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
             final boolean isPrimaryInstance) {
         mApp = app;
+        mModelDbController = new ModelDbController(context);
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
                 isPrimaryInstance);
@@ -153,6 +157,10 @@
         return mModelDelegate;
     }
 
+    public ModelDbController getModelDbController() {
+        return mModelDbController;
+    }
+
     /**
      * Adds the provided items to the workspace.
      */
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index dee3205..0df4bd4 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -50,8 +50,6 @@
 
     public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
 
-    protected ModelDbController mModelDbController;
-
     /**
      * $ adb shell dumpsys activity provider com.android.launcher3
      */
@@ -69,7 +67,6 @@
         if (FeatureFlags.IS_STUDIO_BUILD) {
             Log.d(TAG, "Launcher process started");
         }
-        mModelDbController = new ModelDbController(getContext());
 
         // The content provider exists for the entire duration of the launcher main process and
         // is the first component to get created.
@@ -77,6 +74,10 @@
         return true;
     }
 
+    public ModelDbController getModelDbController() {
+        return LauncherAppState.getInstance(getContext()).getModel().getModelDbController();
+    }
+
     @Override
     public String getType(Uri uri) {
         SqlArguments args = new SqlArguments(uri, null, null);
@@ -95,7 +96,7 @@
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
         qb.setTables(args.table);
 
-        Cursor result = mModelDbController.query(
+        Cursor result = getModelDbController().query(
                 args.table, projection, args.where, args.args, sortOrder);
         result.setNotificationUri(getContext().getContentResolver(), uri);
         return result;
@@ -120,7 +121,7 @@
         }
 
         SqlArguments args = new SqlArguments(uri);
-        int rowId = mModelDbController.insert(args.table, initialValues);
+        int rowId = getModelDbController().insert(args.table, initialValues);
         if (rowId < 0) return null;
 
         uri = ContentUris.withAppendedId(uri, rowId);
@@ -130,7 +131,7 @@
 
     private boolean initializeExternalAdd(ContentValues values) {
         // 1. Ensure that externally added items have a valid item id
-        int id = mModelDbController.generateNewItemId();
+        int id = getModelDbController().generateNewItemId();
         values.put(LauncherSettings.Favorites._ID, id);
 
         // 2. In the case of an app widget, and if no app widget id is specified, we
@@ -171,7 +172,7 @@
     @Override
     public int bulkInsert(Uri uri, ContentValues[] values) {
         SqlArguments args = new SqlArguments(uri);
-        mModelDbController.bulkInsert(args.table, values);
+        getModelDbController().bulkInsert(args.table, values);
         reloadLauncherIfExternal();
         return values.length;
     }
@@ -180,7 +181,7 @@
     @Override
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
-        try (SQLiteTransaction t = mModelDbController.newTransaction()) {
+        try (SQLiteTransaction t = getModelDbController().newTransaction()) {
             final int numOperations = operations.size();
             final ContentProviderResult[] results = new ContentProviderResult[numOperations];
             for (int i = 0; i < numOperations; i++) {
@@ -196,7 +197,7 @@
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
-        int count = mModelDbController.delete(args.table, args.where, args.args);
+        int count = getModelDbController().delete(args.table, args.where, args.args);
         if (count > 0) {
             reloadLauncherIfExternal();
         }
@@ -206,7 +207,7 @@
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
-        int count = mModelDbController.update(args.table, values, args.where, args.args);
+        int count = getModelDbController().update(args.table, values, args.where, args.args);
         reloadLauncherIfExternal();
         return count;
     }
@@ -219,67 +220,59 @@
 
         switch (method) {
             case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
-                mModelDbController.clearEmptyDbFlag();
+                getModelDbController().clearEmptyDbFlag();
                 return null;
             }
             case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
                 Bundle result = new Bundle();
                 result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.deleteEmptyFolders().toArray());
+                        getModelDbController().deleteEmptyFolders().toArray());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
                 Bundle result = new Bundle();
                 result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.generateNewItemId());
+                        getModelDbController().generateNewItemId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
                 Bundle result = new Bundle();
                 result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.getNewScreenId());
+                        getModelDbController().getNewScreenId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
-                mModelDbController.createEmptyDB();
-                return null;
-            }
-            case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
-                mModelDbController.setUseTestWorkspaceLayout(arg);
-                return null;
-            }
-            case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
-                mModelDbController.setUseTestWorkspaceLayout(null);
+                getModelDbController().createEmptyDB();
                 return null;
             }
             case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
-                mModelDbController.loadDefaultFavoritesIfNecessary();
+                getModelDbController().loadDefaultFavoritesIfNecessary();
                 return null;
             }
             case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: {
-                mModelDbController.removeGhostWidgets();
+                getModelDbController().removeGhostWidgets();
                 return null;
             }
             case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: {
                 Bundle result = new Bundle();
                 result.putBinder(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.newTransaction());
+                        getModelDbController().newTransaction());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: {
-                mModelDbController.refreshHotseatRestoreTable();
+                getModelDbController().refreshHotseatRestoreTable();
                 return null;
             }
             case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
                 Bundle result = new Bundle();
                 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.updateCurrentOpenHelper(arg /* dbFile */));
+                        getModelDbController().updateCurrentOpenHelper(arg /* dbFile */));
                 return result;
             }
             case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
                 Bundle result = new Bundle();
                 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                        mModelDbController.prepareForPreview(arg /* dbFile */));
+                        getModelDbController().prepareForPreview(arg /* dbFile */));
                 return result;
             }
         }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 82dbe53..52d814c 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -363,15 +363,6 @@
 
         public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
 
-        public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
-                "set_use_test_workspace_layout_flag";
-        public static final String ARG_DEFAULT_WORKSPACE_LAYOUT_TEST = "default_test_workspace";
-        public static final String ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2 = "default_test2_workspace";
-        public static final String ARG_DEFAULT_WORKSPACE_LAYOUT_TAPL = "default_tapl_workspace";
-
-        public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
-                "clear_use_test_workspace_layout_flag";
-
         public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
 
         public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
@@ -388,6 +379,10 @@
 
         public static final String EXTRA_DB_NAME = "db_name";
 
+        public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob";
+        public static final String LAYOUT_DIGEST_LABEL = "launcher-layout";
+        public static final String LAYOUT_DIGEST_TAG = "ignore";
+
         public static Bundle call(ContentResolver cr, String method) {
             return call(cr, method, null /* arg */);
         }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 924e800..bedb41c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -272,7 +272,7 @@
                     + " to have better visuals");
 
     public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V3 = getDebugFlag(270395186,
-            "ENABLE_DOWNLOAD_APP_UX_V3", DISABLED, "Updates the download app UX"
+            "ENABLE_DOWNLOAD_APP_UX_V3", ENABLED, "Updates the download app UX"
                     + " to have better visuals, improve contrast, and color");
 
     public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(270395077,
@@ -362,6 +362,10 @@
             "Enables the ability to create and save app pairs on the Home screen for easy"
                     + " split screen launching.");
 
+    public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
+            "ENABLE_CURSOR_HOVER_STATES", DISABLED,
+            "Enables cursor hover states for certain elements.");
+
     public static class BooleanFlag {
 
         private final boolean mCurrentValue;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 0fe79e7..d366c4a 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V3;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -34,6 +35,8 @@
 import android.graphics.Rect;
 import android.util.Property;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
@@ -73,12 +76,11 @@
     // Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE
     private static final float COMPLETE_ANIM_FRACTION = 1f;
 
-    private static final float SMALL_SCALE = 0.7f;
+    private static final float SMALL_SCALE = ENABLE_DOWNLOAD_APP_UX_V3.get() ? 0.8f : 0.7f;
     private static final float PROGRESS_STROKE_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get()
-            ? 0.0655f
+            ? 0.055f
             : 0.075f;
     private static final float PROGRESS_BOUNDS_SCALE = 0.075f;
-
     private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
     private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
 
@@ -97,6 +99,11 @@
     private final int mIndicatorColor;
     private final int mSystemAccentColor;
     private final int mSystemBackgroundColor;
+
+    private int mProgressColor;
+    private int mTrackColor;
+    private int mPlateColor;
+
     private final boolean mIsDarkMode;
 
     private float mTrackLength;
@@ -137,8 +144,35 @@
 
         mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+        if (ENABLE_DOWNLOAD_APP_UX_V3.get()) {
+            mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
+        }
         mIndicatorColor = indicatorColor;
 
+        // This is the color
+        int primaryIconColor = mItem.bitmap.color;
+
+        // Progress color
+        float[] m3HCT = new float[3];
+        ColorUtils.colorToM3HCT(primaryIconColor, m3HCT);
+        mProgressColor = ColorUtils.M3HCTtoColor(
+                m3HCT[0],
+                m3HCT[1],
+                isDarkMode ? Math.max(m3HCT[2], 55) : Math.min(m3HCT[2], 40));
+
+        // Track color
+        mTrackColor = ColorUtils.M3HCTtoColor(
+                m3HCT[0],
+                16,
+                isDarkMode ? 30 : 90
+        );
+        // Plate color
+        mPlateColor = ColorUtils.M3HCTtoColor(
+                m3HCT[0],
+                isDarkMode ? 36 : 24,
+                isDarkMode ? (isThemed() ? 10 : 20) : 80
+        );
+
         mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
         mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
         mIsDarkMode = isDarkMode;
@@ -156,10 +190,7 @@
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
 
-
-        float progressWidth = bounds.width() * (ENABLE_DOWNLOAD_APP_UX_V2.get()
-                ? PROGRESS_BOUNDS_SCALE
-                : PROGRESS_STROKE_SCALE);
+        float progressWidth = bounds.width() * PROGRESS_BOUNDS_SCALE;
         mTmpMatrix.setScale(
                 (bounds.width() - 2 * progressWidth) / DEFAULT_PATH_SIZE,
                 (bounds.height() - 2 * progressWidth) / DEFAULT_PATH_SIZE);
@@ -181,20 +212,32 @@
             return;
         }
 
-        if (!ENABLE_DOWNLOAD_APP_UX_V2.get() && mInternalStateProgress > 0) {
+        if (mInternalStateProgress > 0
+                && (ENABLE_DOWNLOAD_APP_UX_V3.get() || !ENABLE_DOWNLOAD_APP_UX_V2.get())) {
             // Draw background.
-            mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
-            mProgressPaint.setColor(mSystemBackgroundColor);
+            mProgressPaint.setStyle(ENABLE_DOWNLOAD_APP_UX_V3.get()
+                    ? Paint.Style.FILL
+                    : Paint.Style.FILL_AND_STROKE);
+            mProgressPaint.setColor(ENABLE_DOWNLOAD_APP_UX_V3.get()
+                    ? mPlateColor
+                    : mSystemBackgroundColor);
             canvas.drawPath(mScaledTrackPath, mProgressPaint);
         }
 
         if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) {
             // Draw track and progress.
             mProgressPaint.setStyle(Paint.Style.STROKE);
-            mProgressPaint.setColor(mSystemAccentColor);
-            mProgressPaint.setAlpha(TRACK_ALPHA);
+            mProgressPaint.setColor(ENABLE_DOWNLOAD_APP_UX_V3.get()
+                    ? mTrackColor
+                    : mSystemAccentColor);
+            if (!ENABLE_DOWNLOAD_APP_UX_V3.get()) {
+                mProgressPaint.setAlpha(TRACK_ALPHA);
+            }
             canvas.drawPath(mScaledTrackPath, mProgressPaint);
             mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
+            if (ENABLE_DOWNLOAD_APP_UX_V3.get()) {
+                mProgressPaint.setColor(mProgressColor);
+            }
             canvas.drawPath(mScaledProgressPath, mProgressPaint);
         }
 
@@ -234,6 +277,10 @@
     public void maybePerformFinishedAnimation(
             PreloadIconDrawable oldIcon, Runnable onFinishCallback) {
 
+        mProgressColor = oldIcon.mProgressColor;
+        mTrackColor = oldIcon.mTrackColor;
+        mPlateColor = oldIcon.mPlateColor;
+
         if (oldIcon.mInternalStateProgress >= 1) {
             mInternalStateProgress = oldIcon.mInternalStateProgress;
         }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index c237f5b..a5dccc1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -65,6 +65,7 @@
 
     private final LongSparseArray<UserHandle> allUsers;
 
+    private final LauncherAppState mApp;
     private final Uri mContentUri;
     private final Context mContext;
     private final PackageManager mPM;
@@ -111,6 +112,7 @@
             UserManagerState userManagerState) {
         super(cursor);
 
+        mApp = app;
         allUsers = userManagerState.allUsers;
         mContentUri = contentUri;
         mContext = app.getContext();
@@ -388,6 +390,7 @@
      */
     public ContentWriter updater() {
        return new ContentWriter(mContext, new ContentWriter.CommitParams(
+               mApp.getModel().getModelDbController().getDatabaseHelper(),
                BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
     }
 
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 7452bcd..97bce8c 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -15,38 +15,51 @@
  */
 package com.android.launcher3.model;
 
+import static android.util.Base64.NO_PADDING;
+import static android.util.Base64.NO_WRAP;
+
 import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
 import static com.android.launcher3.model.DatabaseHelper.EMPTY_DATABASE_CREATED;
 import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Base64;
 import android.util.Log;
 import android.util.Xml;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.AutoInstallsLayout.SourceResources;
 import com.android.launcher3.DefaultLayoutParser;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -69,14 +82,7 @@
 public class ModelDbController {
     private static final String TAG = "LauncherProvider";
 
-    private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
-    private static final int TEST2_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test2_workspace;
-    private static final int TAPL_WORKSPACE_LAYOUT_RES_XML = R.xml.default_tapl_test_workspace;
-
     protected DatabaseHelper mOpenHelper;
-    protected String mProviderAuthority;
-
-    private int mDefaultWorkspaceLayoutOverride = 0;
 
     private final Context mContext;
 
@@ -111,6 +117,7 @@
     /**
      * Refer {@link SQLiteDatabase#query}
      */
+    @WorkerThread
     public Cursor query(String table, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         createDbIfNotExists();
@@ -127,6 +134,7 @@
     /**
      * Refer {@link SQLiteDatabase#insert(String, String, ContentValues)}
      */
+    @WorkerThread
     public int insert(String table, ContentValues initialValues) {
         createDbIfNotExists();
 
@@ -142,6 +150,7 @@
     /**
      * Similar to insert but for adding multiple values in a transaction.
      */
+    @WorkerThread
     public int bulkInsert(String table, ContentValues[] values) {
         createDbIfNotExists();
 
@@ -163,6 +172,7 @@
     /**
      * Refer {@link SQLiteDatabase#delete(String, String, String[])}
      */
+    @WorkerThread
     public int delete(String table, String selection, String[] selectionArgs) {
         createDbIfNotExists();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -181,6 +191,7 @@
     /**
      * Refer {@link SQLiteDatabase#update(String, ContentValues, String, String[])}
      */
+    @WorkerThread
     public int update(String table, ContentValues values,
             String selection, String[] selectionArgs) {
         createDbIfNotExists();
@@ -194,6 +205,7 @@
     /**
      * Clears a previously set flag corresponding to empty db creation
      */
+    @WorkerThread
     public void clearEmptyDbFlag() {
         createDbIfNotExists();
         clearFlagEmptyDbCreated();
@@ -202,6 +214,7 @@
     /**
      * Generates an id to be used for new item in the favorites table
      */
+    @WorkerThread
     public int generateNewItemId() {
         createDbIfNotExists();
         return mOpenHelper.generateNewItemId();
@@ -210,6 +223,7 @@
     /**
      * Generates an id to be used for new workspace screen
      */
+    @WorkerThread
     public int getNewScreenId() {
         createDbIfNotExists();
         return mOpenHelper.getNewScreenId();
@@ -218,29 +232,16 @@
     /**
      * Creates an empty DB clearing all existing data
      */
+    @WorkerThread
     public void createEmptyDB() {
         createDbIfNotExists();
         mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
     }
 
     /**
-     * Overrides the default xml to be used for setting up workspace
-     */
-    public void setUseTestWorkspaceLayout(@Nullable String layout) {
-        if (LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST.equals(layout)) {
-            mDefaultWorkspaceLayoutOverride = TEST_WORKSPACE_LAYOUT_RES_XML;
-        } else if (LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2.equals(layout)) {
-            mDefaultWorkspaceLayoutOverride = TEST2_WORKSPACE_LAYOUT_RES_XML;
-        } else if (LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TAPL.equals(layout)) {
-            mDefaultWorkspaceLayoutOverride = TAPL_WORKSPACE_LAYOUT_RES_XML;
-        } else {
-            mDefaultWorkspaceLayoutOverride = 0;
-        }
-    }
-
-    /**
      * Removes any widget which are present in the framework, but not in out internal DB
      */
+    @WorkerThread
     public void removeGhostWidgets() {
         createDbIfNotExists();
         mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
@@ -249,6 +250,7 @@
     /**
      * Returns a new {@link SQLiteTransaction}
      */
+    @WorkerThread
     public SQLiteTransaction newTransaction() {
         createDbIfNotExists();
         return new SQLiteTransaction(mOpenHelper.getWritableDatabase());
@@ -257,6 +259,7 @@
     /**
      * Refreshes the internal state corresponding to presence of hotseat table
      */
+    @WorkerThread
     public void refreshHotseatRestoreTable() {
         createDbIfNotExists();
         mOpenHelper.mHotseatRestoreTableExists = tableExists(
@@ -267,6 +270,7 @@
      * Updates the current DB and copies all the existing data to the temp table
      * @param dbFile name of the target db file name
      */
+    @WorkerThread
     public boolean updateCurrentOpenHelper(String dbFile) {
         createDbIfNotExists();
         return prepForMigration(
@@ -281,6 +285,7 @@
      * Returns the current DatabaseHelper.
      * Only for tests
      */
+    @WorkerThread
     public DatabaseHelper getDatabaseHelper() {
         createDbIfNotExists();
         return mOpenHelper;
@@ -289,6 +294,7 @@
     /**
      * Prepares the DB for preview by copying all existing data to preview table
      */
+    @WorkerThread
     public boolean prepareForPreview(String dbFile) {
         createDbIfNotExists();
         return prepForMigration(
@@ -307,6 +313,7 @@
      * Deletes any empty folder from the DB.
      * @return Ids of deleted folders.
      */
+    @WorkerThread
     public IntArray deleteEmptyFolders() {
         createDbIfNotExists();
 
@@ -349,6 +356,7 @@
      *   3) From a partner configuration APK, already in the system image
      *   4) The default configuration for the particular device
      */
+    @WorkerThread
     public synchronized void loadDefaultFavoritesIfNecessary() {
         createDbIfNotExists();
         SharedPreferences sp = LauncherPrefs.getPrefs(mContext);
@@ -403,39 +411,55 @@
      */
     private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(
             LauncherWidgetHolder widgetHolder) {
-        final String authority;
-        if (!TextUtils.isEmpty(mProviderAuthority)) {
-            authority = mProviderAuthority;
-        } else {
-            authority = Settings.Secure.getString(mContext.getContentResolver(),
-                    "launcher3.layout.provider");
+        ContentResolver cr = mContext.getContentResolver();
+        String blobHandlerDigest = Settings.Secure.getString(cr, LAYOUT_DIGEST_KEY);
+        if (Utilities.ATLEAST_R && !TextUtils.isEmpty(blobHandlerDigest)) {
+            BlobStoreManager blobManager = mContext.getSystemService(BlobStoreManager.class);
+            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+                    blobManager.openBlob(BlobHandle.createWithSha256(
+                            Base64.decode(blobHandlerDigest, NO_WRAP | NO_PADDING),
+                            LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG)))) {
+                return getAutoInstallsLayoutFromIS(in, widgetHolder, new SourceResources() { });
+            } catch (Exception e) {
+                Log.e(TAG, "Error getting layout from blob handle" , e);
+                return null;
+            }
         }
+
+        String authority = Settings.Secure.getString(cr, "launcher3.layout.provider");
         if (TextUtils.isEmpty(authority)) {
             return null;
         }
 
-        ProviderInfo pi = mContext.getPackageManager().resolveContentProvider(authority, 0);
+        PackageManager pm = mContext.getPackageManager();
+        ProviderInfo pi = pm.resolveContentProvider(authority, 0);
         if (pi == null) {
             Log.e(TAG, "No provider found for authority " + authority);
             return null;
         }
         Uri uri = getLayoutUri(authority, mContext);
-        try (InputStream in = mContext.getContentResolver().openInputStream(uri)) {
-            // Read the full xml so that we fail early in case of any IO error.
-            String layout = new String(IOUtils.toByteArray(in));
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(new StringReader(layout));
-
+        try (InputStream in = cr.openInputStream(uri)) {
             Log.d(TAG, "Loading layout from " + authority);
-            return new AutoInstallsLayout(mContext, widgetHolder, mOpenHelper,
-                    mContext.getPackageManager().getResourcesForApplication(pi.applicationInfo),
-                    () -> parser, AutoInstallsLayout.TAG_WORKSPACE);
+
+            Resources res = pm.getResourcesForApplication(pi.applicationInfo);
+            return getAutoInstallsLayoutFromIS(in, widgetHolder, SourceResources.wrap(res));
         } catch (Exception e) {
             Log.e(TAG, "Error getting layout stream from: " + authority , e);
             return null;
         }
     }
 
+    private AutoInstallsLayout getAutoInstallsLayoutFromIS(InputStream in,
+            LauncherWidgetHolder widgetHolder, SourceResources res) throws Exception {
+        // Read the full xml so that we fail early in case of any IO error.
+        String layout = new String(IOUtils.toByteArray(in));
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new StringReader(layout));
+
+        return new AutoInstallsLayout(mContext, widgetHolder, mOpenHelper, res,
+                () -> parser, AutoInstallsLayout.TAG_WORKSPACE);
+    }
+
     private static Uri getLayoutUri(String authority, Context ctx) {
         InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
         return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
@@ -448,13 +472,9 @@
 
     private DefaultLayoutParser getDefaultLayoutParser(LauncherWidgetHolder widgetHolder) {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
-        int defaultLayout = mDefaultWorkspaceLayoutOverride > 0
-                ? mDefaultWorkspaceLayoutOverride : idp.defaultLayoutId;
-
-        if (mContext.getSystemService(UserManager.class).isDemoUser()
-                && idp.demoModeLayoutId != 0) {
-            defaultLayout = idp.demoModeLayoutId;
-        }
+        int defaultLayout = idp.demoModeLayoutId != 0
+                && mContext.getSystemService(UserManager.class).isDemoUser()
+                ? idp.demoModeLayoutId : idp.defaultLayoutId;
 
         return new DefaultLayoutParser(mContext, widgetHolder,
                 mOpenHelper, mContext.getResources(), defaultLayout);
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 772ffa4..ddb8b05 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -33,7 +33,6 @@
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.Utilities;
@@ -267,8 +266,7 @@
             item.onAddToDatabase(writer);
             writer.put(Favorites._ID, item.id);
 
-            cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
-
+            mModel.getModelDbController().insert(Favorites.TABLE_NAME, writer.getValues(mContext));
             synchronized (mBgDataModel) {
                 checkItemInfoLocked(item.id, item, stackTrace);
                 mBgDataModel.addItem(mContext, item, true);
@@ -324,13 +322,13 @@
         notifyDelete(Collections.singleton(info));
 
         enqueueDeleteRunnable(() -> {
-            ContentResolver cr = mContext.getContentResolver();
-            cr.delete(LauncherSettings.Favorites.CONTENT_URI,
-                    LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
+            mModel.getModelDbController().delete(Favorites.TABLE_NAME,
+                    Favorites.CONTAINER + "=" + info.id, null);
             mBgDataModel.removeItem(mContext, info.contents);
             info.contents.clear();
 
-            cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
+            mModel.getModelDbController().delete(Favorites.TABLE_NAME,
+                    Favorites._ID + "=" + info.id, null);
             mBgDataModel.removeItem(mContext, info);
             verifier.verifyModel();
         });
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index ba5249c..ac72164 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -107,7 +107,7 @@
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             RestoreDbTask task = new RestoreDbTask();
             task.sanitizeDB(context, helper, db, new BackupManager(context));
-            task.restoreAppWidgetIdsIfExists(context);
+            task.restoreAppWidgetIdsIfExists(context, helper);
             t.commit();
             return true;
         } catch (Exception e) {
@@ -321,11 +321,11 @@
                 .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
     }
 
-    private void restoreAppWidgetIdsIfExists(Context context) {
+    private void restoreAppWidgetIdsIfExists(Context context, DatabaseHelper helper) {
         LauncherPrefs lp = LauncherPrefs.get(context);
         if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
             AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
-            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
+            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, helper,
                     IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
                     IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
                     host);
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 55c2585..e509235 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -19,13 +19,14 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.UserHandle;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.model.DatabaseHelper;
 import com.android.launcher3.pm.UserCache;
 
 /**
@@ -105,7 +106,8 @@
 
     public int commit() {
         if (mCommitParams != null) {
-            return mContext.getContentResolver().update(mCommitParams.mUri, getValues(mContext),
+            mCommitParams.mDatabaseHelper.getWritableDatabase().update(
+                    Favorites.TABLE_NAME, getValues(mContext),
                     mCommitParams.mWhere, mCommitParams.mSelectionArgs);
         }
         return 0;
@@ -113,12 +115,12 @@
 
     public static final class CommitParams {
 
-        final Uri mUri;
+        final DatabaseHelper mDatabaseHelper;
         final String mWhere;
         final String[] mSelectionArgs;
 
-        public CommitParams(String where, String[] selectionArgs) {
-            mUri = LauncherSettings.Favorites.CONTENT_URI;
+        public CommitParams(DatabaseHelper helper, String where, String[] selectionArgs) {
+            mDatabaseHelper = helper;
             mWhere = where;
             mSelectionArgs = selectionArgs;
         }
diff --git a/tests/Android.bp b/tests/Android.bp
index f9d6f04..fa0cdf2 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -45,6 +45,7 @@
       "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
+      "src/com/android/launcher3/util/LauncherLayoutBuilder.java",
       "src/com/android/launcher3/util/TestUtil.java",
       "src/com/android/launcher3/util/Wait.java",
       "src/com/android/launcher3/util/WidgetUtils.java",
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index a37c3cd..dc835e2 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -106,11 +106,6 @@
     public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
     public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
     public static final String REQUEST_CLEAR_DATA = "clear-data";
-    public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
-    public static final String REQUEST_USE_TEST2_WORKSPACE_LAYOUT = "use-test2-workspace-layout";
-    public static final String REQUEST_USE_TAPL_WORKSPACE_LAYOUT = "use-tapl-workspace-layout";
-    public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
-            "use-default-workspace-layout";
     public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
     public static final String REQUEST_IS_TABLET = "is-tablet";
     public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 217bec3..81f1525 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -54,12 +54,14 @@
 import com.android.launcher3.tapl.HomeAppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.TISBindRule;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -83,6 +85,8 @@
     @Rule
     public TISBindRule mTISBindRule = new TISBindRule();
 
+    private AutoCloseable mLauncherLayout;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -101,6 +105,13 @@
         AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
+    }
+
     // Please don't add negative test cases for methods that fail only after a long wait.
     public static void expectFail(String message, Runnable action) {
         boolean failed = false;
@@ -230,8 +241,10 @@
     @Test
     @ScreenRecord // b/202433017
     public void testWorkspace() throws Exception {
-        // Make sure there is an instance of chrome on the hotseat
-        mLauncher.useTaplWorkspaceLayoutOnReload();
+        // Set workspace  that includes the chrome Activity app icon on the hotseat.
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+                .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
         clearLauncherData();
 
         final Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index 3abafdf..c4b6d43 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -30,6 +30,8 @@
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.TestUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -49,12 +51,24 @@
 @RunWith(AndroidJUnit4.class)
 public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
 
+    private AutoCloseable mLauncherLayout;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mLauncher.useTest2WorkspaceLayoutOnReload();
-        TaplTestsLauncher3.initialize(this);
 
+        // Set layout that includes Maps/Play on workspace, and Messaging/Chrome on hotseat.
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+                .atHotseat(0).putApp(
+                        "com.google.android.apps.messaging",
+                        "com.google.android.apps.messaging.ui.ConversationListActivity")
+                .atHotseat(1).putApp("com.android.chrome", "com.google.android.apps.chrome.Main")
+                .atWorkspace(0, -1, 0).putApp(
+                        "com.google.android.apps.maps", "com.google.android.maps.MapsActivity")
+                .atWorkspace(3, -1, 0).putApp(
+                        "com.android.vending", "com.android.vending.AssetBrowserActivity");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
+        TaplTestsLauncher3.initialize(this);
         assumeTrue(mLauncher.isTwoPanels());
 
         // Pre verifying the screens
@@ -67,9 +81,11 @@
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Exception {
         executeOnLauncher(launcher -> launcher.enableHotseatEdu(true));
-        mLauncher.useDefaultWorkspaceLayoutOnReload();
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 9e88c06..bf31e39 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -62,7 +62,6 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
@@ -423,12 +422,11 @@
 
         @Override
         public boolean onCreate() {
-            mModelDbController = new ModelDbController(getContext());
             return true;
         }
 
         public SQLiteDatabase getDb() {
-            return mModelDbController.getDatabaseHelper().getWritableDatabase();
+            return getModelDbController().getDatabaseHelper().getWritableDatabase();
         }
     }
 
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 433fd31..4981795 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -15,15 +15,29 @@
  */
 package com.android.launcher3.util;
 
+import static android.util.Base64.NO_PADDING;
+import static android.util.Base64.NO_WRAP;
+
 import static androidx.test.InstrumentationRegistry.getContext;
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
+
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Resources;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Base64;
 
 import androidx.test.uiautomator.UiDevice;
 
@@ -36,6 +50,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Predicate;
 import java.util.function.ToIntFunction;
@@ -109,6 +125,35 @@
                 "pm uninstall " + DUMMY_PACKAGE);
     }
 
+    /**
+     * Sets the default layout for Launcher and returns an object which can be used to clear
+     * the data
+     */
+    public static AutoCloseable setLauncherDefaultLayout(
+            Context context, LauncherLayoutBuilder layoutBuilder) throws Exception {
+        byte[] data = layoutBuilder.build().getBytes();
+        byte[] digest = MessageDigest.getInstance("SHA-256").digest(data);
+
+        BlobHandle handle = BlobHandle.createWithSha256(
+                digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG);
+        BlobStoreManager blobManager = context.getSystemService(BlobStoreManager.class);
+        final long sessionId = blobManager.createSession(handle);
+        CountDownLatch wait = new CountDownLatch(1);
+        try (BlobStoreManager.Session session = blobManager.openSession(sessionId)) {
+            try (OutputStream out = new AutoCloseOutputStream(session.openWrite(0, -1))) {
+                out.write(data);
+            }
+            session.allowPublicAccess();
+            session.commit(AsyncTask.THREAD_POOL_EXECUTOR, i -> wait.countDown());
+        }
+
+        String key = Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
+        Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, key);
+        wait.await();
+        return () ->
+            Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
+    }
+
     private static class PackageInstallCheck extends LauncherApps.Callback
             implements AutoCloseable {
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9905603..ba8f070 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1851,36 +1851,6 @@
         getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
     }
 
-    /**
-     * Reloads the workspace with a test layout that includes the Test Activity app icon on the
-     * hotseat.
-     */
-    public void useTestWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
-    }
-
-    /**
-     * Reloads the workspace with a test layout that includes Maps/Play on workspace, and
-     * Dialer/Messaging/Chrome/Camera on hotseat.
-     */
-    public void useTest2WorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TEST2_WORKSPACE_LAYOUT);
-    }
-
-
-    /**
-     * Reloads the workspace with a test layout that includes the chrome Activity app icon on the
-     * hotseat.
-     */
-    public void useTaplWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TAPL_WORKSPACE_LAYOUT);
-    }
-
-    /** Reloads the workspace with the default layout defined by the user's grid size selection. */
-    public void useDefaultWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
-    }
-
     /** Shows the taskbar if it is hidden, otherwise does nothing. */
     public void showTaskbarIfHidden() {
         getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);